Started merging the JS and QML editor plug-ins.

This commit is contained in:
Erik Verbruggen
2010-01-15 17:20:03 +01:00
parent 898e84ceff
commit 96e31c2a0f
81 changed files with 243 additions and 11009 deletions

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="application/x-qml">
<sub-class-of type="text/plain"/>
<comment>QML file</comment>
<glob pattern="*.qml"/>
</mime-type>
<mime-type type="application/javascript">
<alias type="application/x-javascript"/>
<alias type="text/javascript"/>
<sub-class-of type="text/plain"/>
<comment>Qt Script file</comment>
<glob pattern="*.js"/>
<glob pattern="*.qs"/>
</mime-type>
</mime-info>

View File

@@ -0,0 +1,20 @@
<plugin name="QmlJSEditor" version="1.3.80" compatVersion="1.3.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2008-2009 Nokia Corporation</copyright>
<license>
Commercial Usage
Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia.
GNU Lesser General Public License Usage
Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
</license>
<description>Editor for QML and JavaScript.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
<dependency name="Core" version="1.3.80"/>
<dependency name="TextEditor" version="1.3.80"/>
<dependency name="Help" version="1.3.80"/>
</dependencyList>
</plugin>

View File

@@ -0,0 +1,230 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlcodecompletion.h"
#include "qmljseditor.h"
#include "qmlmodelmanagerinterface.h"
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include <qmljs/qmlsymbol.h>
#include <texteditor/basetexteditor.h>
#include <QtDebug>
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, Qml::QmlTypeSystem *typeSystem, QObject *parent)
: TextEditor::ICompletionCollector(parent),
m_modelManager(modelManager),
m_editor(0),
m_startPosition(0),
m_caseSensitivity(Qt::CaseSensitive),
m_typeSystem(typeSystem)
{
Q_ASSERT(modelManager);
Q_ASSERT(typeSystem);
}
QmlCodeCompletion::~QmlCodeCompletion()
{ }
Qt::CaseSensitivity QmlCodeCompletion::caseSensitivity() const
{ return m_caseSensitivity; }
void QmlCodeCompletion::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
{ m_caseSensitivity = caseSensitivity; }
bool QmlCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
{
if (qobject_cast<QmlJSTextEditor *>(editor->widget()))
return true;
return false;
}
bool QmlCodeCompletion::triggersCompletion(TextEditor::ITextEditable *)
{ return false; }
int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
{
m_editor = editor;
QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(m_editor->widget());
if (! edit)
return -1;
int pos = editor->position();
while (editor->characterAt(pos - 1).isLetterOrNumber() || editor->characterAt(pos - 1) == QLatin1Char('_'))
--pos;
m_startPosition = pos;
m_completions.clear();
Qml::QmlDocument::Ptr qmlDocument = edit->qmlDocument();
// qDebug() << "*** document:" << qmlDocument;
if (qmlDocument.isNull())
return pos;
if (!qmlDocument->program())
qmlDocument = m_modelManager->snapshot().value(qmlDocument->fileName());
// FIXME: this completion strategy is not going to work when the document was never parsed correctly.
if (qmlDocument && qmlDocument->program()) {
QmlJS::AST::UiProgram *program = qmlDocument->program();
// qDebug() << "*** program:" << program;
if (program) {
QmlExpressionUnderCursor expressionUnderCursor;
QTextCursor cursor(edit->document());
cursor.setPosition(pos);
expressionUnderCursor(cursor, qmlDocument);
QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot(), m_typeSystem);
QmlResolveExpression resolver(context);
// qDebug()<<"*** expression under cursor:"<<expressionUnderCursor.expressionNode();
QList<Qml::QmlSymbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
// qDebug()<<"***"<<symbols.size()<<"visible symbols";
foreach (Qml::QmlSymbol *symbol, symbols) {
QString word;
if (symbol->isIdSymbol()) {
word = symbol->asIdSymbol()->id();
} else {
word = symbol->name();
}
if (word.isEmpty())
continue;
TextEditor::CompletionItem item(this);
item.text = word;
m_completions.append(item);
}
}
}
return pos;
}
void QmlCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
{
// ### FIXME: this code needs to be generalized.
const int length = m_editor->position() - m_startPosition;
if (length == 0)
*completions = m_completions;
else if (length > 0) {
const QString key = m_editor->textAt(m_startPosition, length);
/*
* This code builds a regular expression in order to more intelligently match
* camel-case style. This means upper-case characters will be rewritten as follows:
*
* A => [a-z0-9_]*A (for any but the first capital letter)
*
* Meaning it allows any sequence of lower-case characters to preceed an
* upper-case character. So for example gAC matches getActionController.
*/
QString keyRegExp;
keyRegExp += QLatin1Char('^');
bool first = true;
foreach (const QChar &c, key) {
if (c.isUpper() && !first) {
keyRegExp += QLatin1String("[a-z0-9_]*");
keyRegExp += c;
} else if (m_caseSensitivity == Qt::CaseInsensitive && c.isLower()) {
keyRegExp += QLatin1Char('[');
keyRegExp += c;
keyRegExp += c.toUpper();
keyRegExp += QLatin1Char(']');
} else {
keyRegExp += QRegExp::escape(c);
}
first = false;
}
const QRegExp regExp(keyRegExp, Qt::CaseSensitive);
foreach (TextEditor::CompletionItem item, m_completions) {
if (regExp.indexIn(item.text) == 0) {
item.relevance = (key.length() > 0 &&
item.text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0;
(*completions) << item;
}
}
}
}
void QmlCodeCompletion::complete(const TextEditor::CompletionItem &item)
{
const QString toInsert = item.text;
const int length = m_editor->position() - m_startPosition;
m_editor->setCurPos(m_startPosition);
m_editor->replace(length, toInsert);
}
bool QmlCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems)
{
if (completionItems.count() == 1) {
complete(completionItems.first());
return true;
} else {
// Compute common prefix
QString firstKey = completionItems.first().text;
QString lastKey = completionItems.last().text;
const int length = qMin(firstKey.length(), lastKey.length());
firstKey.truncate(length);
lastKey.truncate(length);
while (firstKey != lastKey) {
firstKey.chop(1);
lastKey.chop(1);
}
int typedLength = m_editor->position() - m_startPosition;
if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
m_editor->setCurPos(m_startPosition);
m_editor->replace(typedLength, firstKey);
}
}
return false;
}
void QmlCodeCompletion::cleanup()
{
m_editor = 0;
m_startPosition = 0;
m_completions.clear();
}

View File

@@ -0,0 +1,78 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLCODECOMPLETION_H
#define QMLCODECOMPLETION_H
#include <qmljs/qmltypesystem.h>
#include <texteditor/icompletioncollector.h>
namespace TextEditor {
class ITextEditable;
}
namespace QmlJSEditor {
class QmlModelManagerInterface;
namespace Internal {
class QmlCodeCompletion: public TextEditor::ICompletionCollector
{
Q_OBJECT
public:
QmlCodeCompletion(QmlModelManagerInterface *modelManager, Qml::QmlTypeSystem *typeSystem, QObject *parent = 0);
virtual ~QmlCodeCompletion();
Qt::CaseSensitivity caseSensitivity() const;
void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity);
virtual bool supportsEditor(TextEditor::ITextEditable *editor);
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
virtual int startCompletion(TextEditor::ITextEditable *editor);
virtual void completions(QList<TextEditor::CompletionItem> *completions);
virtual void complete(const TextEditor::CompletionItem &item);
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
virtual void cleanup();
private:
QmlModelManagerInterface *m_modelManager;
TextEditor::ITextEditable *m_editor;
int m_startPosition;
QList<TextEditor::CompletionItem> m_completions;
Qt::CaseSensitivity m_caseSensitivity;
Qml::QmlTypeSystem *m_typeSystem;
};
} // end of namespace Internal
} // end of namespace QmlJSEditor
#endif // QMLCODECOMPLETION_H

View File

@@ -0,0 +1,119 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include <QtCore/QDebug>
#include "qmlcompletionvisitor.h"
#include "qmljsast_p.h"
using namespace QmlJS;
using namespace QmlJS::AST;
namespace QmlEditor {
namespace Internal {
QmlCompletionVisitor::QmlCompletionVisitor()
{
}
QSet<QString> QmlCompletionVisitor::operator()(QmlJS::AST::UiProgram *ast, int pos)
{
m_completions.clear();
m_pos = (quint32) pos;
Node::acceptChild(ast, this);
return m_completions;
}
bool QmlCompletionVisitor::preVisit(QmlJS::AST::Node *node)
{
if (!m_parentStack.isEmpty())
m_nodeParents[node] = m_parentStack.top();
m_parentStack.push(node);
return true;
}
static QString toString(Statement *stmt)
{
if (ExpressionStatement *exprStmt = AST::cast<ExpressionStatement*>(stmt)) {
if (IdentifierExpression *idExpr = AST::cast<IdentifierExpression *>(exprStmt->expression)) {
return idExpr->name->asString();
}
}
return QString();
}
bool QmlCompletionVisitor::visit(UiScriptBinding *ast)
{
if (!ast)
return false;
UiObjectDefinition *parentObject = findParentObject(ast);
if (ast->qualifiedId && ast->qualifiedId->name->asString() == QLatin1String("id")) {
const QString nodeId = toString(ast->statement);
if (!nodeId.isEmpty())
m_objectToId[parentObject] = nodeId;
} else if (m_objectToId.contains(parentObject)) {
if (ast->qualifiedId && ast->qualifiedId->name) {
const QString parentId = m_objectToId[parentObject];
m_completions.insert(parentId + "." + ast->qualifiedId->name->asString());
}
}
if (ast->firstSourceLocation().begin() >= m_pos && m_pos <= ast->lastSourceLocation().end()) {
UiObjectDefinition *parentsParent = findParentObject(parentObject);
if (parentsParent) {
m_completions.insert(QLatin1String("parent"));
}
}
return true;
}
UiObjectDefinition *QmlCompletionVisitor::findParentObject(Node *node) const
{
if (!node)
return 0;
Node *candidate = m_nodeParents[node];
if (candidate == 0)
return 0;
if (UiObjectDefinition *parentObject = AST::cast<UiObjectDefinition *>(candidate))
return parentObject;
else
return findParentObject(candidate);
}
} // namespace Internal
} // namespace QmlEditor

View File

@@ -0,0 +1,72 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef COMPLETIONVISITOR_H
#define COMPLETIONVISITOR_H
#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtCore/QStack>
#include <QtCore/QString>
#include "qmljsastfwd_p.h"
#include "qmljsastvisitor_p.h"
#include "qmljsengine_p.h"
namespace QmlEditor {
namespace Internal {
class QmlCompletionVisitor: protected QmlJS::AST::Visitor
{
public:
QmlCompletionVisitor();
QSet<QString> operator()(QmlJS::AST::UiProgram *ast, int pos);
protected:
virtual bool preVisit(QmlJS::AST::Node *node);
virtual void postVisit(QmlJS::AST::Node *) { m_parentStack.pop(); }
virtual bool visit(QmlJS::AST::UiScriptBinding *ast);
private:
QmlJS::AST::UiObjectDefinition *findParentObject(QmlJS::AST::Node *node) const;
private:
QSet<QString> m_completions;
quint32 m_pos;
QStack<QmlJS::AST::Node *> m_parentStack;
QMap<QmlJS::AST::Node *, QmlJS::AST::Node *> m_nodeParents;
QMap<QmlJS::AST::Node *, QString> m_objectToId;
};
} // namespace Internal
} // namespace QmlEditor
#endif // COMPLETIONVISITOR_H

View File

@@ -0,0 +1,308 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlexpressionundercursor.h"
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <qmljs/parser/qmljslexer_p.h>
#include <qmljs/parser/qmljsnodepool_p.h>
#include <qmljs/parser/qmljsparser_p.h>
#include <QDebug>
using namespace Qml;
using namespace QmlJS;
using namespace QmlJS::AST;
namespace QmlJSEditor {
namespace Internal {
class PositionCalculator: protected Visitor
{
public:
Node *operator()(Node *ast, int pos)
{
_pos = pos;
_expression = 0;
_offset = -1;
_length = -1;
Node::accept(ast, this);
return _expression;
}
int offset() const
{ return _offset; }
int length() const
{ return _length; }
protected:
bool visit(FieldMemberExpression *ast)
{
if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) {
_expression = ast;
_offset = ast->identifierToken.offset;
_length = ast->identifierToken.length;
}
return true;
}
bool visit(IdentifierExpression *ast)
{
if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) {
_expression = ast;
_offset = ast->firstSourceLocation().offset;
_length = ast->lastSourceLocation().end() - _offset;
}
return false;
}
bool visit(UiImport * /*ast*/)
{
return false;
}
bool visit(UiQualifiedId *ast)
{
if (ast->identifierToken.offset <= _pos) {
for (UiQualifiedId *iter = ast; iter; iter = iter->next) {
if (_pos <= iter->identifierToken.end()) {
// found it
_expression = ast;
_offset = ast->identifierToken.offset;
for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) {
_length = iter2->identifierToken.end() - _offset;
}
break;
}
}
}
return false;
}
private:
quint32 _pos;
Node *_expression;
int _offset;
int _length;
};
class ScopeCalculator: protected Visitor
{
public:
QStack<QmlSymbol*> operator()(const QmlDocument::Ptr &doc, int pos)
{
_doc = doc;
_pos = pos;
_scopes.clear();
_currentSymbol = 0;
Node::accept(doc->program(), this);
return _scopes;
}
protected:
virtual bool visit(Block * /*ast*/)
{
// TODO
// if (_pos > ast->lbraceToken.end() && _pos < ast->rbraceToken.offset) {
// push(ast);
// Node::accept(ast->statements, this);
// }
return false;
}
virtual bool visit(UiObjectBinding *ast)
{
if (ast->initializer && ast->initializer->lbraceToken.offset < _pos && _pos <= ast->initializer->rbraceToken.end()) {
push(ast);
Node::accept(ast->initializer, this);
}
return false;
}
virtual bool visit(UiObjectDefinition *ast)
{
if (ast->initializer && ast->initializer->lbraceToken.offset < _pos && _pos <= ast->initializer->rbraceToken.end()) {
push(ast);
Node::accept(ast->initializer, this);
}
return false;
}
virtual bool visit(UiArrayBinding *ast)
{
if (ast->lbracketToken.offset < _pos && _pos <= ast->rbracketToken.end()) {
push(ast);
Node::accept(ast->members, this);
}
return false;
}
private:
void push(Node *node)
{
QmlSymbolFromFile* symbol;
if (_currentSymbol) {
symbol = _currentSymbol->findMember(node);
} else {
symbol = _doc->findSymbol(node);
}
if (symbol) {
_currentSymbol = symbol;
if (!cast<UiArrayBinding*>(node))
_scopes.push(symbol);
}
}
private:
QmlDocument::Ptr _doc;
quint32 _pos;
QStack<QmlSymbol*> _scopes;
QmlSymbolFromFile* _currentSymbol;
};
}
}
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
QmlExpressionUnderCursor::QmlExpressionUnderCursor()
: _expressionNode(0),
_pos(0), _engine(0), _nodePool(0)
{
}
QmlExpressionUnderCursor::~QmlExpressionUnderCursor()
{
if (_engine) { delete _engine; _engine = 0; }
if (_nodePool) { delete _nodePool; _nodePool = 0; }
}
void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor,
const QmlDocument::Ptr &doc)
{
if (_engine) { delete _engine; _engine = 0; }
if (_nodePool) { delete _nodePool; _nodePool = 0; }
_pos = cursor.position();
_expressionNode = 0;
_expressionOffset = -1;
_expressionLength = -1;
_expressionScopes.clear();
const QTextBlock block = cursor.block();
parseExpression(block);
if (_expressionOffset != -1) {
ScopeCalculator calculator;
_expressionScopes = calculator(doc, _expressionOffset);
}
}
void QmlExpressionUnderCursor::parseExpression(const QTextBlock &block)
{
int textPosition = _pos - block.position();
const QString blockText = block.text();
if (textPosition > 0) {
if (blockText.at(textPosition - 1) == QLatin1Char('.'))
--textPosition;
} else {
textPosition = 0;
}
const QString text = blockText.left(textPosition);
Node *node = 0;
if (UiObjectMember *binding = tryBinding(text)) {
// qDebug() << "**** binding";
node = binding;
} else if (Statement *stmt = tryStatement(text)) {
// qDebug() << "**** statement";
node = stmt;
} else {
// qDebug() << "**** none";
}
if (node) {
PositionCalculator calculator;
_expressionNode = calculator(node, textPosition);
_expressionOffset = calculator.offset() + block.position();
_expressionLength = calculator.length();
}
}
Statement *QmlExpressionUnderCursor::tryStatement(const QString &text)
{
_engine = new Engine();
_nodePool = new NodePool("", _engine);
Lexer lexer(_engine);
Parser parser(_engine);
lexer.setCode(text, /*line = */ 1);
if (parser.parseStatement())
return parser.statement();
else
return 0;
}
UiObjectMember *QmlExpressionUnderCursor::tryBinding(const QString &text)
{
_engine = new Engine();
_nodePool = new NodePool("", _engine);
Lexer lexer(_engine);
Parser parser(_engine);
lexer.setCode(text, /*line = */ 1);
if (parser.parseUiObjectMember()) {
UiObjectMember *member = parser.uiObjectMember();
if (cast<UiObjectBinding*>(member) || cast<UiArrayBinding*>(member) || cast<UiScriptBinding*>(member))
return member;
else
return 0;
} else {
return 0;
}
}

View File

@@ -0,0 +1,88 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLEXPRESSIONUNDERCURSOR_H
#define QMLEXPRESSIONUNDERCURSOR_H
#include <qmljs/parser/qmljsastfwd_p.h>
#include <qmljs/qmldocument.h>
#include <qmljs/qmlsymbol.h>
#include <QStack>
#include <QTextBlock>
#include <QTextCursor>
namespace QmlJS {
class Engine;
class NodePool;
}
namespace QmlJSEditor {
namespace Internal {
class QmlExpressionUnderCursor
{
public:
QmlExpressionUnderCursor();
virtual ~QmlExpressionUnderCursor();
void operator()(const QTextCursor &cursor, const Qml::QmlDocument::Ptr &doc);
QStack<Qml::QmlSymbol *> expressionScopes() const
{ return _expressionScopes; }
QmlJS::AST::Node *expressionNode() const
{ return _expressionNode; }
int expressionOffset() const
{ return _expressionOffset; }
int expressionLength() const
{ return _expressionLength; }
private:
void parseExpression(const QTextBlock &block);
QmlJS::AST::Statement *tryStatement(const QString &text);
QmlJS::AST::UiObjectMember *tryBinding(const QString &text);
private:
QStack<Qml::QmlSymbol *> _expressionScopes;
QmlJS::AST::Node *_expressionNode;
int _expressionOffset;
int _expressionLength;
quint32 _pos;
QmlJS::Engine *_engine;
QmlJS::NodePool *_nodePool;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLEXPRESSIONUNDERCURSOR_H

View File

@@ -0,0 +1,74 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditorconstants.h"
#include "qmlfilewizard.h"
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
using namespace QmlJSEditor;
QmlFileWizard::QmlFileWizard(const BaseFileWizardParameters &parameters,
QObject *parent):
Core::StandardFileWizard(parameters, parent)
{
}
Core::GeneratedFiles QmlFileWizard::generateFilesFromPath(const QString &path,
const QString &name,
QString * /*errorMessage*/) const
{
const QString mimeType = QLatin1String(Constants::QMLJSEDITOR_MIMETYPE);
const QString fileName = Core::BaseFileWizard::buildFileName(path, name, preferredSuffix(mimeType));
Core::GeneratedFile file(fileName);
file.setEditorId(QLatin1String(Constants::C_QMLJSEDITOR_ID));
file.setContents(fileContents(fileName));
return Core::GeneratedFiles() << file;
}
QString QmlFileWizard::fileContents(const QString &fileName) const
{
const QString baseName = QFileInfo(fileName).completeBaseName();
QString contents;
QTextStream str(&contents);
// str << CppTools::AbstractEditorSupport::licenseTemplate();
str << QLatin1String("import Qt 4.6\n")
<< QLatin1String("\n")
<< QLatin1String("Rectangle {\n")
<< QLatin1String(" width: 640\n")
<< QLatin1String(" height: 480\n")
<< QLatin1String("}\n");
return contents;
}

View File

@@ -0,0 +1,58 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLFILEWIZARD_H
#define QMLFILEWIZARD_H
#include <coreplugin/basefilewizard.h>
namespace QmlJSEditor {
class QmlFileWizard: public Core::StandardFileWizard
{
Q_OBJECT
public:
typedef Core::BaseFileWizardParameters BaseFileWizardParameters;
QmlFileWizard(const BaseFileWizardParameters &parameters,
QObject *parent = 0);
protected:
QString fileContents(const QString &baseName) const;
protected:
Core::GeneratedFiles generateFilesFromPath(const QString &path,
const QString &fileName,
QString *errorMessage) const;
};
} // namespace QmlJSEditor
#endif // QMLFILEWIZARD_H

View File

@@ -0,0 +1,114 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlhighlighter.h"
#include <utils/qtcassert.h>
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
QmlHighlighter::QmlHighlighter(QTextDocument *parent) :
SharedTools::QScriptHighlighter(true, parent)
{
m_currentBlockParentheses.reserve(20);
m_braceDepth = 0;
QSet<QString> qmlKeywords(keywords());
qmlKeywords << QLatin1String("property");
qmlKeywords << QLatin1String("signal");
qmlKeywords << QLatin1String("readonly");
m_scanner.setKeywords(qmlKeywords);
}
int QmlHighlighter::onBlockStart()
{
m_currentBlockParentheses.clear();
m_braceDepth = 0;
int state = 0;
int previousState = previousBlockState();
if (previousState != -1) {
state = previousState & 0xff;
m_braceDepth = previousState >> 8;
}
return state;
}
void QmlHighlighter::onOpeningParenthesis(QChar parenthesis, int pos)
{
if (parenthesis == QLatin1Char('{')
|| parenthesis == QLatin1Char('['))
++m_braceDepth;
m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Opened, parenthesis, pos));
}
void QmlHighlighter::onClosingParenthesis(QChar parenthesis, int pos)
{
if (parenthesis == QLatin1Char('}')
|| parenthesis == QLatin1Char(']'))
--m_braceDepth;
m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Closed, parenthesis, pos));
}
void QmlHighlighter::onBlockEnd(int state, int firstNonSpace)
{
typedef TextEditor::TextBlockUserData TextEditorBlockData;
setCurrentBlockState((m_braceDepth << 8) | state);
// Set block data parentheses. Force creation of block data unless empty
TextEditorBlockData *blockData = 0;
if (QTextBlockUserData *userData = currentBlockUserData())
blockData = static_cast<TextEditorBlockData *>(userData);
if (!blockData && !m_currentBlockParentheses.empty()) {
blockData = new TextEditorBlockData;
setCurrentBlockUserData(blockData);
}
if (blockData) {
blockData->setParentheses(m_currentBlockParentheses);
blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse);
blockData->setCollapseMode(TextEditor::TextBlockUserData::NoCollapse);
}
if (!m_currentBlockParentheses.isEmpty()) {
QTC_ASSERT(blockData, return);
int collapse = Parenthesis::collapseAtPos(m_currentBlockParentheses);
if (collapse >= 0) {
if (collapse == firstNonSpace)
blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseThis);
else
blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseAfter);
}
if (Parenthesis::hasClosingCollapse(m_currentBlockParentheses))
blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse);
}
}

View File

@@ -0,0 +1,64 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLSYNTAXHIGHLIGHTER_H
#define QMLSYNTAXHIGHLIGHTER_H
#include <qscripthighlighter/qscripthighlighter.h>
#include <texteditor/basetexteditor.h>
namespace QmlJSEditor {
namespace Internal {
// Highlighter for Scripts that stores
// the parentheses encountered in the block data
// for parentheses matching to work.
class QmlHighlighter : public SharedTools::QScriptHighlighter
{
Q_OBJECT
public:
QmlHighlighter(QTextDocument *parent = 0);
private:
virtual int onBlockStart();
virtual void onOpeningParenthesis(QChar parenthesis, int pos);
virtual void onClosingParenthesis(QChar parenthesis, int pos);
virtual void onBlockEnd(int state, int firstNonSpace);
typedef TextEditor::Parenthesis Parenthesis;
typedef TextEditor::Parentheses Parentheses;
Parentheses m_currentBlockParentheses;
int m_braceDepth;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLSYNTAXHIGHLIGHTER_H

View File

@@ -0,0 +1,227 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditor.h"
#include "qmlexpressionundercursor.h"
#include "qmlhoverhandler.h"
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <debugger/debuggerconstants.h>
#include <extensionsystem/pluginmanager.h>
#include <qmljs/qmlsymbol.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
#include <QtGui/QToolTip>
#include <QtGui/QTextCursor>
#include <QtGui/QTextBlock>
#include <QtHelp/QHelpEngineCore>
using namespace Core;
using namespace Qml;
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
QmlHoverHandler::QmlHoverHandler(QObject *parent)
: QObject(parent)
, m_helpEngineNeedsSetup(false)
{
m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<QmlModelManagerInterface>();
ICore *core = ICore::instance();
QFileInfo fi(core->settings()->fileName());
// FIXME shouldn't the help engine create the directory if it doesn't exist?
QDir directory(fi.absolutePath()+"/qtcreator");
if (!directory.exists())
directory.mkpath(directory.absolutePath());
m_helpEngine = new QHelpEngineCore(directory.absolutePath()
+ QLatin1String("/helpcollection.qhc"), this);
//m_helpEngine->setAutoSaveFilter(false);
if (!m_helpEngine->setupData())
qWarning() << "Could not initialize help engine:" << m_helpEngine->error();
m_helpEngine->setCurrentFilter(tr("Unfiltered"));
m_helpEngineNeedsSetup = m_helpEngine->registeredDocumentations().count() == 0;
// Listen for editor opened events in order to connect to tooltip/helpid requests
connect(core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
this, SLOT(editorOpened(Core::IEditor *)));
}
void QmlHoverHandler::editorOpened(IEditor *editor)
{
QmlJSEditorEditable *qmlEditor = qobject_cast<QmlJSEditorEditable *>(editor);
if (!qmlEditor)
return;
connect(qmlEditor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*, QPoint, int)),
this, SLOT(showToolTip(TextEditor::ITextEditor*, QPoint, int)));
connect(qmlEditor, SIGNAL(contextHelpIdRequested(TextEditor::ITextEditor*, int)),
this, SLOT(updateContextHelpId(TextEditor::ITextEditor*, int)));
}
void QmlHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos)
{
if (! editor)
return;
ICore *core = ICore::instance();
const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER);
if (core->hasContext(dbgcontext))
return;
updateHelpIdAndTooltip(editor, pos);
if (m_toolTip.isEmpty())
QToolTip::hideText();
else {
const QPoint pnt = point - QPoint(0,
#ifdef Q_WS_WIN
24
#else
16
#endif
);
QToolTip::showText(pnt, m_toolTip);
}
}
void QmlHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos)
{
updateHelpIdAndTooltip(editor, pos);
}
static QString buildHelpId(QmlSymbol *symbol)
{
if (!symbol)
return QString();
const QString idTemplate(QLatin1String("QML.%1"));
return idTemplate.arg(symbol->name());
}
void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos)
{
m_helpId.clear();
m_toolTip.clear();
if (!m_modelManager)
return;
QmlJSTextEditor *scriptEditor = qobject_cast<QmlJSTextEditor *>(editor->widget());
if (!scriptEditor)
return;
const Snapshot documents = m_modelManager->snapshot();
const QString fileName = editor->file()->fileName();
QmlDocument::Ptr doc = documents.value(fileName);
if (!doc)
return; // nothing to do
QTextCursor tc(scriptEditor->document());
tc.setPosition(pos);
const unsigned lineNumber = tc.block().blockNumber() + 1;
// We only want to show F1 if the tooltip matches the help id
bool showF1 = true;
foreach (const QmlJS::DiagnosticMessage &m, scriptEditor->diagnosticMessages()) {
if (m.loc.startLine == lineNumber) {
m_toolTip = m.message;
showF1 = false;
break;
}
}
QString symbolName = QLatin1String("<unknown>");
if (m_helpId.isEmpty()) {
// Move to the end of a qualified name
bool stop = false;
while (!stop) {
const QChar ch = editor->characterAt(tc.position());
if (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.')) {
tc.setPosition(tc.position() + 1);
} else {
stop = true;
}
}
// Fetch the expression's code
QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(tc, doc);
Qml::QmlTypeSystem *typeSystem = ExtensionSystem::PluginManager::instance()->getObject<Qml::QmlTypeSystem>();
QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, m_modelManager->snapshot(), typeSystem);
QmlResolveExpression resolver(context);
QmlSymbol *resolvedSymbol = resolver.typeOf(expressionUnderCursor.expressionNode());
if (resolvedSymbol) {
symbolName = resolvedSymbol->name();
if (resolvedSymbol->isBuildInSymbol())
m_helpId = buildHelpId(resolvedSymbol);
else if (QmlSymbolFromFile *symbolFromFile = resolvedSymbol->asSymbolFromFile())
m_toolTip = symbolFromFile->fileName();
}
}
if (m_helpEngineNeedsSetup && m_helpEngine->registeredDocumentations().count() > 0) {
m_helpEngine->setupData();
m_helpEngineNeedsSetup = false;
}
if (!m_toolTip.isEmpty())
m_toolTip = Qt::escape(m_toolTip);
if (!m_helpId.isEmpty() && !m_helpEngine->linksForIdentifier(m_helpId).isEmpty()) {
if (showF1) {
m_toolTip = QString(QLatin1String("<table><tr><td valign=middle><nobr>%1</td>"
"<td><img src=\":/cppeditor/images/f1.svg\"></td></tr></table>"))
.arg(m_toolTip);
}
editor->setContextHelpId(m_helpId);
} else if (!m_toolTip.isEmpty()) {
m_toolTip = QString(QLatin1String("<nobr>%1")).arg(m_toolTip);
} else if (!m_helpId.isEmpty()) {
m_toolTip = QString(QLatin1String("<nobr>No help available for \"%1\"")).arg(symbolName);
}
}

View File

@@ -0,0 +1,81 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLHOVERHANDLER_H
#define QMLHOVERHANDLER_H
#include "qmlmodelmanagerinterface.h"
#include <QtCore/QObject>
QT_BEGIN_NAMESPACE
class QHelpEngineCore;
class QPoint;
QT_END_NAMESPACE
namespace Core {
class IEditor;
}
namespace TextEditor {
class ITextEditor;
}
namespace QmlJSEditor {
namespace Internal {
class QmlHoverHandler : public QObject
{
Q_OBJECT
public:
QmlHoverHandler(QObject *parent = 0);
public slots:
void showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos);
void updateContextHelpId(TextEditor::ITextEditor *editor, int pos);
private slots:
void editorOpened(Core::IEditor *editor);
private:
void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos);
private:
QmlModelManagerInterface *m_modelManager;
QHelpEngineCore *m_helpEngine;
QString m_helpId;
QString m_toolTip;
bool m_helpEngineNeedsSetup;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLHOVERHANDLER_H

View File

@@ -0,0 +1,866 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditor.h"
#include "qmljseditorconstants.h"
#include "qmlhighlighter.h"
#include "qmljseditorplugin.h"
#include "qmlmodelmanager.h"
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include <qscripthighlighter/qscriptindenter.h>
#include <qmljs/qmltypesystem.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <qmljs/qmldocument.h>
#include <qmljs/qmlidcollector.h>
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/uniqueidmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <texteditor/basetextdocument.h>
#include <texteditor/fontsettings.h>
#include <texteditor/textblockiterator.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <utils/changeset.h>
#include <utils/uncommentselection.h>
#include <QtCore/QTimer>
#include <QtGui/QMenu>
#include <QtGui/QComboBox>
#include <QtGui/QInputDialog>
#include <QtGui/QMainWindow>
#include <QtGui/QToolBar>
enum {
UPDATE_DOCUMENT_DEFAULT_INTERVAL = 250,
UPDATE_USES_DEFAULT_INTERVAL = 150
};
using namespace Qml;
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace SharedTools;
namespace {
int blockBraceDepth(const QTextBlock &block)
{
int state = block.userState();
if (state == -1)
return 0;
return (state >> 8) & 0xFF;
}
int blockStartState(const QTextBlock &block)
{
int state = block.userState();
if (state == -1)
return 0;
else
return state & 0xff;
}
} // end of anonymous namespace
namespace QmlJSEditor {
namespace Internal {
class FindIdDeclarations: protected Visitor
{
public:
typedef QMap<QString, QList<AST::SourceLocation> > Result;
Result operator()(AST::Node *node)
{
_ids.clear();
_maybeIds.clear();
accept(node);
return _ids;
}
protected:
QString asString(AST::UiQualifiedId *id)
{
QString text;
for (; id; id = id->next) {
if (id->name)
text += id->name->asString();
else
text += QLatin1Char('?');
if (id->next)
text += QLatin1Char('.');
}
return text;
}
void accept(AST::Node *node)
{ AST::Node::acceptChild(node, this); }
using Visitor::visit;
using Visitor::endVisit;
virtual bool visit(AST::UiScriptBinding *node)
{
if (asString(node->qualifiedId) == QLatin1String("id")) {
if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node->statement)) {
if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(stmt->expression)) {
if (idExpr->name) {
const QString id = idExpr->name->asString();
QList<AST::SourceLocation> *locs = &_ids[id];
locs->append(idExpr->firstSourceLocation());
locs->append(_maybeIds.value(id));
_maybeIds.remove(id);
return false;
}
}
}
}
accept(node->statement);
return false;
}
virtual bool visit(AST::IdentifierExpression *node)
{
if (node->name) {
const QString name = node->name->asString();
if (_ids.contains(name))
_ids[name].append(node->identifierToken);
else
_maybeIds[name].append(node->identifierToken);
}
return false;
}
private:
Result _ids;
Result _maybeIds;
};
class FindDeclarations: protected Visitor
{
QList<Declaration> _declarations;
int _depth;
public:
QList<Declaration> operator()(AST::Node *node)
{
_depth = -1;
_declarations.clear();
accept(node);
return _declarations;
}
protected:
using Visitor::visit;
using Visitor::endVisit;
QString asString(AST::UiQualifiedId *id)
{
QString text;
for (; id; id = id->next) {
if (id->name)
text += id->name->asString();
else
text += QLatin1Char('?');
if (id->next)
text += QLatin1Char('.');
}
return text;
}
void accept(AST::Node *node)
{ AST::Node::acceptChild(node, this); }
void init(Declaration *decl, AST::UiObjectMember *member)
{
const SourceLocation first = member->firstSourceLocation();
const SourceLocation last = member->lastSourceLocation();
decl->startLine = first.startLine;
decl->startColumn = first.startColumn;
decl->endLine = last.startLine;
decl->endColumn = last.startColumn + last.length;
}
virtual bool visit(AST::UiObjectDefinition *node)
{
++_depth;
Declaration decl;
init(&decl, node);
decl.text.fill(QLatin1Char(' '), _depth);
if (node->qualifiedTypeNameId)
decl.text.append(asString(node->qualifiedTypeNameId));
else
decl.text.append(QLatin1Char('?'));
_declarations.append(decl);
return true; // search for more bindings
}
virtual void endVisit(AST::UiObjectDefinition *)
{
--_depth;
}
virtual bool visit(AST::UiObjectBinding *node)
{
++_depth;
Declaration decl;
init(&decl, node);
decl.text.fill(QLatin1Char(' '), _depth);
decl.text.append(asString(node->qualifiedId));
decl.text.append(QLatin1String(": "));
if (node->qualifiedTypeNameId)
decl.text.append(asString(node->qualifiedTypeNameId));
else
decl.text.append(QLatin1Char('?'));
_declarations.append(decl);
return true; // search for more bindings
}
virtual void endVisit(AST::UiObjectBinding *)
{
--_depth;
}
#if 0 // ### ignore script bindings for now.
virtual bool visit(AST::UiScriptBinding *node)
{
++_depth;
Declaration decl;
init(&decl, node);
decl.text.fill(QLatin1Char(' '), _depth);
decl.text.append(asString(node->qualifiedId));
_declarations.append(decl);
return false; // more more bindings in this subtree.
}
virtual void endVisit(AST::UiScriptBinding *)
{
--_depth;
}
#endif
};
QmlJSEditorEditable::QmlJSEditorEditable(QmlJSTextEditor *editor)
: BaseTextEditorEditable(editor)
{
Core::UniqueIDManager *uidm = Core::UniqueIDManager::instance();
m_context << uidm->uniqueIdentifier(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
m_context << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
}
QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
TextEditor::BaseTextEditor(parent),
m_methodCombo(0),
m_modelManager(0),
m_typeSystem(0)
{
m_idsRevision = -1;
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
setCodeFoldingSupported(true);
setCodeFoldingVisible(true);
setMimeType(QmlJSEditor::Constants::QMLJSEDITOR_MIMETYPE);
m_updateDocumentTimer = new QTimer(this);
m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
m_updateDocumentTimer->setSingleShot(true);
connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
m_updateUsesTimer = new QTimer(this);
m_updateUsesTimer->setInterval(UPDATE_USES_DEFAULT_INTERVAL);
m_updateUsesTimer->setSingleShot(true);
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateDocument()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
baseTextDocument()->setSyntaxHighlighter(new QmlHighlighter);
m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<QmlModelManagerInterface>();
m_typeSystem = ExtensionSystem::PluginManager::instance()->getObject<Qml::QmlTypeSystem>();
if (m_modelManager) {
connect(m_modelManager, SIGNAL(documentUpdated(Qml::QmlDocument::Ptr)),
this, SLOT(onDocumentUpdated(Qml::QmlDocument::Ptr)));
}
}
QmlJSTextEditor::~QmlJSTextEditor()
{
}
QList<Declaration> QmlJSTextEditor::declarations() const
{ return m_declarations; }
Core::IEditor *QmlJSEditorEditable::duplicate(QWidget *parent)
{
QmlJSTextEditor *newEditor = new QmlJSTextEditor(parent);
newEditor->duplicateFrom(editor());
QmlJSEditorPlugin::instance()->initializeEditor(newEditor);
return newEditor->editableInterface();
}
QString QmlJSEditorEditable::id() const
{
return QLatin1String(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
}
QmlJSTextEditor::Context QmlJSEditorEditable::context() const
{
return m_context;
}
void QmlJSTextEditor::updateDocument()
{
m_updateDocumentTimer->start(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
}
void QmlJSTextEditor::updateDocumentNow()
{
// ### move in the parser thread.
m_updateDocumentTimer->stop();
const QString fileName = file()->fileName();
m_modelManager->updateSourceFiles(QStringList() << fileName);
}
void QmlJSTextEditor::onDocumentUpdated(Qml::QmlDocument::Ptr doc)
{
if (file()->fileName() != doc->fileName())
return;
m_document = doc;
FindIdDeclarations updateIds;
m_idsRevision = document()->revision();
m_ids = updateIds(doc->program());
if (doc->isParsedCorrectly()) {
FindDeclarations findDeclarations;
m_declarations = findDeclarations(doc->program());
QStringList items;
items.append(tr("<Select Symbol>"));
foreach (Declaration decl, m_declarations)
items.append(decl.text);
m_methodCombo->clear();
m_methodCombo->addItems(items);
updateMethodBoxIndex();
}
QList<QTextEdit::ExtraSelection> selections;
QTextCharFormat errorFormat;
errorFormat.setUnderlineColor(Qt::red);
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
QTextEdit::ExtraSelection sel;
m_diagnosticMessages = doc->diagnosticMessages();
foreach (const DiagnosticMessage &d, m_diagnosticMessages) {
int line = d.loc.startLine;
int column = d.loc.startColumn;
if (column == 0)
column = 1;
QTextCursor c(document()->findBlockByNumber(line - 1));
sel.cursor = c;
sel.cursor.setPosition(c.position() + column - 1);
if (sel.cursor.atBlockEnd())
sel.cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
else
sel.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
sel.format = errorFormat;
selections.append(sel);
}
setExtraSelections(CodeWarningsSelection, selections);
}
void QmlJSTextEditor::jumpToMethod(int index)
{
if (index) {
Declaration d = m_declarations.at(index - 1);
gotoLine(d.startLine, d.startColumn - 1);
setFocus();
}
}
void QmlJSTextEditor::updateMethodBoxIndex()
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
int currentSymbolIndex = 0;
int index = 0;
while (index < m_declarations.size()) {
const Declaration &d = m_declarations.at(index++);
if (line < d.startLine)
break;
else
currentSymbolIndex = index;
}
m_methodCombo->setCurrentIndex(currentSymbolIndex);
updateUses();
}
void QmlJSTextEditor::updateUses()
{
m_updateUsesTimer->start();
}
void QmlJSTextEditor::updateUsesNow()
{
if (document()->revision() != m_idsRevision) {
updateUses();
return;
}
m_updateUsesTimer->stop();
QList<QTextEdit::ExtraSelection> selections;
foreach (const AST::SourceLocation &loc, m_ids.value(wordUnderCursor())) {
if (! loc.isValid())
continue;
QTextEdit::ExtraSelection sel;
sel.format = m_occurrencesFormat;
sel.cursor = textCursor();
sel.cursor.setPosition(loc.begin());
sel.cursor.setPosition(loc.end(), QTextCursor::KeepAnchor);
selections.append(sel);
}
setExtraSelections(CodeSemanticsSelection, selections);
}
void QmlJSTextEditor::updateMethodBoxToolTip()
{
}
void QmlJSTextEditor::updateFileName()
{
}
void QmlJSTextEditor::renameIdUnderCursor()
{
const QString id = wordUnderCursor();
bool ok = false;
const QString newId = QInputDialog::getText(Core::ICore::instance()->mainWindow(),
tr("Rename..."),
tr("New id:"),
QLineEdit::Normal,
id, &ok);
if (ok) {
Utils::ChangeSet changeSet;
foreach (const AST::SourceLocation &loc, m_ids.value(id)) {
changeSet.replace(loc.offset, loc.length, newId);
}
QTextCursor tc = textCursor();
changeSet.apply(&tc);
}
}
QStringList QmlJSTextEditor::keywords() const
{
QStringList words;
if (QmlHighlighter *highlighter = qobject_cast<QmlHighlighter*>(baseTextDocument()->syntaxHighlighter()))
words = highlighter->keywords().toList();
return words;
}
void QmlJSTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
{
TextEditor::BaseTextEditor::setFontSettings(fs);
QmlHighlighter *highlighter = qobject_cast<QmlHighlighter*>(baseTextDocument()->syntaxHighlighter());
if (!highlighter)
return;
static QVector<QString> categories;
if (categories.isEmpty()) {
categories << QLatin1String(TextEditor::Constants::C_NUMBER)
<< QLatin1String(TextEditor::Constants::C_STRING)
<< QLatin1String(TextEditor::Constants::C_TYPE)
<< QLatin1String(TextEditor::Constants::C_KEYWORD)
<< QLatin1String(TextEditor::Constants::C_PREPROCESSOR)
<< QLatin1String(TextEditor::Constants::C_LABEL)
<< QLatin1String(TextEditor::Constants::C_COMMENT)
<< QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
}
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
m_occurrencesFormat.clearForeground();
highlighter->setFormats(fs.toTextCharFormats(categories));
highlighter->rehighlight();
}
QString QmlJSTextEditor::wordUnderCursor() const
{
QTextCursor tc = textCursor();
tc.movePosition(QTextCursor::StartOfWord);
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
const QString word= tc.selectedText();
return word;
}
bool QmlJSTextEditor::isElectricCharacter(const QChar &ch) const
{
if (ch == QLatin1Char('}')
|| ch == QLatin1Char(']'))
return true;
return false;
}
bool QmlJSTextEditor::isClosingBrace(const QList<QScriptIncrementalScanner::Token> &tokens) const
{
if (tokens.size() == 1) {
const QScriptIncrementalScanner::Token firstToken = tokens.first();
return firstToken.is(QScriptIncrementalScanner::Token::RightBrace) || firstToken.is(QScriptIncrementalScanner::Token::RightBracket);
}
return false;
}
void QmlJSTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
{
TextEditor::TabSettings ts = tabSettings();
SharedTools::QScriptIndenter indenter;
indenter.setTabSize(ts.m_tabSize);
indenter.setIndentSize(ts.m_indentSize);
const int indent = indenter.indentForBottomLine(doc->begin(), block.next(), typedChar);
ts.indentLine(block, indent);
}
TextEditor::BaseTextEditorEditable *QmlJSTextEditor::createEditableInterface()
{
QmlJSEditorEditable *editable = new QmlJSEditorEditable(this);
createToolBar(editable);
return editable;
}
void QmlJSTextEditor::createToolBar(QmlJSEditorEditable *editable)
{
m_methodCombo = new QComboBox;
m_methodCombo->setMinimumContentsLength(22);
//m_methodCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
// Make the combo box prefer to expand
QSizePolicy policy = m_methodCombo->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Expanding);
m_methodCombo->setSizePolicy(policy);
connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
QList<QAction*> actions = toolBar->actions();
toolBar->insertWidget(actions.first(), m_methodCombo);
}
TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/)
{
Link link;
if (!m_modelManager)
return link;
const Snapshot snapshot = m_modelManager->snapshot();
QmlDocument::Ptr doc = snapshot.document(file()->fileName());
if (!doc)
return link;
QTextCursor expressionCursor(cursor);
{
// correct the position by moving to the end of an identifier (if we're hovering over one):
int pos = cursor.position();
forever {
const QChar ch = characterAt(pos);
if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
++pos;
else
break;
}
expressionCursor.setPosition(pos);
}
QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(expressionCursor, doc);
QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot, m_typeSystem);
QmlResolveExpression resolver(context);
QmlSymbol *symbol = resolver.typeOf(expressionUnderCursor.expressionNode());
if (!symbol)
return link;
if (const QmlSymbolFromFile *target = symbol->asSymbolFromFile()) {
link.pos = expressionUnderCursor.expressionOffset();
link.length = expressionUnderCursor.expressionLength();
link.fileName = target->fileName();
link.line = target->line();
link.column = target->column();
if (link.column > 0)
--link.column;
}
return link;
}
void QmlJSTextEditor::contextMenuEvent(QContextMenuEvent *e)
{
QMenu *menu = new QMenu();
if (Core::ActionContainer *mcontext = Core::ICore::instance()->actionManager()->actionContainer(QmlJSEditor::Constants::M_CONTEXT)) {
QMenu *contextMenu = mcontext->menu();
foreach (QAction *action, contextMenu->actions())
menu->addAction(action);
}
const QString id = wordUnderCursor();
const QList<AST::SourceLocation> &locations = m_ids.value(id);
if (! locations.isEmpty()) {
menu->addSeparator();
QAction *a = menu->addAction(tr("Rename id '%1'...").arg(id));
connect(a, SIGNAL(triggered()), this, SLOT(renameIdUnderCursor()));
}
appendStandardContextMenuActions(menu);
menu->exec(e->globalPos());
menu->deleteLater();
}
void QmlJSTextEditor::unCommentSelection()
{
Utils::unCommentSelection(this);
}
static bool isCompleteStringLiteral(const QStringRef &text)
{
if (text.length() < 2)
return false;
const QChar quote = text.at(0);
if (text.at(text.length() - 1) == quote)
return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly.
return false;
}
bool QmlJSTextEditor::contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert) const
{
QChar ch;
if (! textToInsert.isEmpty())
ch = textToInsert.at(0);
switch (ch.unicode()) {
case '\'':
case '"':
case '(':
case '[':
case '{':
case ')':
case ']':
case '}':
case ';':
break;
default:
if (ch.isNull())
break;
return false;
} // end of switch
const QString blockText = cursor.block().text();
const int blockState = blockStartState(cursor.block());
QScriptIncrementalScanner tokenize;
const QList<QScriptIncrementalScanner::Token> tokens = tokenize(blockText, blockState);
const int pos = cursor.columnNumber();
int tokenIndex = 0;
for (; tokenIndex < tokens.size(); ++tokenIndex) {
const QScriptIncrementalScanner::Token &token = tokens.at(tokenIndex);
if (pos >= token.begin()) {
if (pos < token.end())
break;
else if (pos == token.end() && (token.is(QScriptIncrementalScanner::Token::Comment) ||
token.is(QScriptIncrementalScanner::Token::String)))
break;
}
}
if (tokenIndex != tokens.size()) {
const QScriptIncrementalScanner::Token &token = tokens.at(tokenIndex);
switch (token.kind) {
case QScriptIncrementalScanner::Token::Comment:
return false;
case QScriptIncrementalScanner::Token::String: {
const QStringRef tokenText = blockText.midRef(token.offset, token.length);
const QChar quote = tokenText.at(0);
if (ch == quote && isCompleteStringLiteral(tokenText))
break;
return false;
}
default:
break;
} // end of switch
}
return true;
}
bool QmlJSTextEditor::isInComment(const QTextCursor &) const
{
// ### implement me
return false;
}
QString QmlJSTextEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text, const QChar &, int *skippedChars) const
{
if (text.length() != 1)
return QString();
const QChar la = characterAt(tc.position());
const QChar ch = text.at(0);
switch (ch.unicode()) {
case '\'':
if (la != ch)
return QString(ch);
++*skippedChars;
break;
case '"':
if (la != ch)
return QString(ch);
++*skippedChars;
break;
case '(':
return QString(QLatin1Char(')'));
case '[':
return QString(QLatin1Char(']'));
case '{':
return QString(); // nothing to do.
case ')':
case ']':
case '}':
case ';':
if (la == ch)
++*skippedChars;
break;
default:
break;
} // end of switch
return QString();
}
QString QmlJSTextEditor::insertParagraphSeparator(const QTextCursor &) const
{
return QLatin1String("}\n");
}
} // namespace Internal
} // namespace QmlJSEditor

View File

@@ -0,0 +1,169 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLJSEDITOR_H
#define QMLJSEDITOR_H
#include <qmljs/qmldocument.h>
#include <qscripthighlighter/qscriptincrementalscanner.h>
#include <texteditor/basetexteditor.h>
QT_BEGIN_NAMESPACE
class QComboBox;
class QTimer;
QT_END_NAMESPACE
namespace Core {
class ICore;
}
namespace Qml {
class QmlTypeSystem;
}
namespace QmlJSEditor {
class QmlModelManagerInterface;
namespace Internal {
class QmlHighlighter;
class QmlJSTextEditor;
class QmlJSEditorEditable : public TextEditor::BaseTextEditorEditable
{
Q_OBJECT
public:
QmlJSEditorEditable(QmlJSTextEditor *);
QList<int> context() const;
bool duplicateSupported() const { return true; }
Core::IEditor *duplicate(QWidget *parent);
QString id() const;
bool isTemporary() const { return false; }
private:
QList<int> m_context;
};
struct Declaration
{
QString text;
int startLine;
int startColumn;
int endLine;
int endColumn;
Declaration()
: startLine(0),
startColumn(0),
endLine(0),
endColumn(0)
{ }
};
class QmlJSTextEditor : public TextEditor::BaseTextEditor
{
Q_OBJECT
public:
typedef QList<int> Context;
QmlJSTextEditor(QWidget *parent = 0);
~QmlJSTextEditor();
QList<Declaration> declarations() const;
QStringList keywords() const;
QList<QmlJS::DiagnosticMessage> diagnosticMessages() const
{ return m_diagnosticMessages; }
virtual void unCommentSelection();
Qml::QmlDocument::Ptr qmlDocument() const { return m_document; }
public slots:
virtual void setFontSettings(const TextEditor::FontSettings &);
private slots:
void onDocumentUpdated(Qml::QmlDocument::Ptr doc);
void updateDocument();
void updateDocumentNow();
void jumpToMethod(int index);
void updateMethodBoxIndex();
void updateMethodBoxToolTip();
void updateFileName();
void updateUses();
void updateUsesNow();
// refactoring ops
void renameIdUnderCursor();
protected:
void contextMenuEvent(QContextMenuEvent *e);
TextEditor::BaseTextEditorEditable *createEditableInterface();
void createToolBar(QmlJSEditorEditable *editable);
TextEditor::BaseTextEditor::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true);
//// brace matching
virtual bool contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert = QString()) const;
virtual bool isInComment(const QTextCursor &cursor) const;
virtual QString insertMatchingBrace(const QTextCursor &tc, const QString &text, const QChar &la, int *skippedChars) const;
virtual QString insertParagraphSeparator(const QTextCursor &tc) const;
private:
virtual bool isElectricCharacter(const QChar &ch) const;
virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
bool isClosingBrace(const QList<SharedTools::QScriptIncrementalScanner::Token> &tokens) const;
QString wordUnderCursor() const;
const Context m_context;
QTimer *m_updateDocumentTimer;
QTimer *m_updateUsesTimer;
QComboBox *m_methodCombo;
QList<Declaration> m_declarations;
QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap
int m_idsRevision;
QList<QmlJS::DiagnosticMessage> m_diagnosticMessages;
Qml::QmlDocument::Ptr m_document;
QmlModelManagerInterface *m_modelManager;
Qml::QmlTypeSystem *m_typeSystem;
QTextCharFormat m_occurrencesFormat;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLJSEDITOR_H

View File

@@ -0,0 +1,3 @@
include(qmljseditor_dependencies.pri)
LIBS *= -l$$qtLibraryTarget(QmlJSEditor)

View File

@@ -0,0 +1,48 @@
TEMPLATE = lib
TARGET = QmlJSEditor
include(../../qtcreatorplugin.pri)
include(qmljseditor_dependencies.pri)
include(../../shared/qscripthighlighter/qscripthighlighter.pri)
DEPENDPATH += ../../shared/qscripthighlighter
CONFIG += help
DEFINES += \
QMLJSEDITOR_LIBRARY \
QT_CREATOR
HEADERS += \
qmlcodecompletion.h \
qmljseditor.h \
qmljseditor_global.h \
qmljseditoractionhandler.h \
qmljseditorconstants.h \
qmljseditorfactory.h \
qmljseditorplugin.h \
qmlexpressionundercursor.h \
qmlfilewizard.h \
qmlhighlighter.h \
qmlhoverhandler.h \
qmllookupcontext.h \
qmlmodelmanager.h \
qmlmodelmanagerinterface.h \
qmlresolveexpression.h
SOURCES += \
qmlcodecompletion.cpp \
qmljseditor.cpp \
qmljseditoractionhandler.cpp \
qmljseditorfactory.cpp \
qmljseditorplugin.cpp \
qmlexpressionundercursor.cpp \
qmlfilewizard.cpp \
qmlhighlighter.cpp \
qmlhoverhandler.cpp \
qmllookupcontext.cpp \
qmlmodelmanager.cpp \
qmlmodelmanagerinterface.cpp \
qmlresolveexpression.cpp
RESOURCES += qmljseditor.qrc
OTHER_FILES += QmlJSEditor.pluginspec QmlJSEditor.mimetypes.xml

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/qmljseditor" >
<file>QmlJSEditor.mimetypes.xml</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,6 @@
include(../../plugins/coreplugin/coreplugin.pri)
include(../../plugins/texteditor/texteditor.pri)
include(../../plugins/help/help.pri)
include(../../shared/indenter/indenter.pri)
include(../../libs/qmljs/qmljs.pri)
include(../../libs/utils/utils.pri)

View File

@@ -0,0 +1,41 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLJSEDITOR_GLOBAL_H
#define QMLJSEDITOR_GLOBAL_H
#include <QtGlobal>
#if defined(QMLJSEDITOR_LIBRARY)
# define QMLJSEDITOR_EXPORT Q_DECL_EXPORT
#else
# define QMLJSEDITOR_EXPORT Q_DECL_IMPORT
#endif
#endif // QMLJSEDITOR_GLOBAL_H

View File

@@ -0,0 +1,58 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditoractionhandler.h"
#include "qmljseditorconstants.h"
#include "qmljseditor.h"
#include <coreplugin/icore.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <QtCore/QDebug>
#include <QtGui/QAction>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
namespace QmlJSEditor {
namespace Internal {
QmlJSEditorActionHandler::QmlJSEditorActionHandler()
: TextEditor::TextEditorActionHandler(QLatin1String(QmlJSEditor::Constants::C_QMLJSEDITOR_ID),
Format)
{
}
void QmlJSEditorActionHandler::createActions()
{
TextEditor::TextEditorActionHandler::createActions();
}
} // namespace Internal
} // namespace QmlJSEditor

View File

@@ -0,0 +1,51 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLDITORACTIONHANDLER_H
#define QMLDITORACTIONHANDLER_H
#include <texteditor/texteditoractionhandler.h>
namespace QmlJSEditor {
namespace Internal {
class QmlJSEditorActionHandler : public TextEditor::TextEditorActionHandler
{
Q_OBJECT
public:
QmlJSEditorActionHandler();
void createActions();
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLDITORACTIONHANDLER_H

View File

@@ -0,0 +1,49 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLJSEDITOR_CONSTANTS_H
#define QMLJSEDITOR_CONSTANTS_H
#include <QtCore/QtGlobal>
namespace QmlJSEditor {
namespace Constants {
const char * const M_CONTEXT = "QML JS Editor.ContextMenu";
const char * const RUN_SEP = "QmlJSEditor.Run.Separator";
const char * const C_QMLJSEDITOR_ID = "QMLProjectManager.QMLJSEditor";
const char * const C_QMLJSEDITOR_DISPLAY_NAME = QT_TRANSLATE_NOOP("OpenWith::Editors", "QMLJS Editor");
const char * const TASK_INDEX = "QmlJSEditor.TaskIndex";
const char * const QMLJSEDITOR_MIMETYPE = "application/x-qml";
} // namespace Constants
} // namespace QmlJSEditor
#endif // QMLJSEDITOR_CONSTANTS_H

View File

@@ -0,0 +1,85 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditorfactory.h"
#include "qmljseditor.h"
#include "qmljseditoractionhandler.h"
#include "qmljseditorconstants.h"
#include "qmljseditorplugin.h"
#include <coreplugin/editormanager/editormanager.h>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
using namespace QmlJSEditor::Internal;
using namespace QmlJSEditor::Constants;
QmlJSEditorFactory::QmlJSEditorFactory(QObject *parent)
: Core::IEditorFactory(parent),
m_mimeTypes(QLatin1String(QmlJSEditor::Constants::QMLJSEDITOR_MIMETYPE))
{
}
QmlJSEditorFactory::~QmlJSEditorFactory()
{
}
QString QmlJSEditorFactory::id() const
{
return QLatin1String(C_QMLJSEDITOR_ID);
}
QString QmlJSEditorFactory::displayName() const
{
return tr(C_QMLJSEDITOR_DISPLAY_NAME);
}
Core::IFile *QmlJSEditorFactory::open(const QString &fileName)
{
Core::IEditor *iface = Core::EditorManager::instance()->openEditor(fileName, id());
if (!iface) {
qWarning() << "QmlEditorFactory::open: openEditor failed for " << fileName;
return 0;
}
return iface->file();
}
Core::IEditor *QmlJSEditorFactory::createEditor(QWidget *parent)
{
QmlJSTextEditor *rc = new QmlJSTextEditor(parent);
QmlJSEditorPlugin::instance()->initializeEditor(rc);
return rc->editableInterface();
}
QStringList QmlJSEditorFactory::mimeTypes() const
{
return m_mimeTypes;
}

View File

@@ -0,0 +1,68 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLJSEDITORFACTORY_H
#define QMLJSEDITORFACTORY_H
#include <coreplugin/editormanager/ieditorfactory.h>
#include <QtCore/QStringList>
namespace TextEditor {
class TextEditorActionHandler;
}
namespace QmlJSEditor {
namespace Internal {
class QmlJSEditorActionHandler;
class QmlJSEditorFactory : public Core::IEditorFactory
{
Q_OBJECT
public:
QmlJSEditorFactory(QObject *parent);
~QmlJSEditorFactory();
virtual QStringList mimeTypes() const;
// IEditorFactory
QString id() const;
QString displayName() const;
Core::IFile *open(const QString &fileName);
Core::IEditor *createEditor(QWidget *parent);
private:
const QStringList m_mimeTypes;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLJSEDITORFACTORY_H

View File

@@ -0,0 +1,182 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljseditorplugin.h"
#include "qmlhighlighter.h"
#include "qmljseditor.h"
#include "qmljseditorconstants.h"
#include "qmljseditorfactory.h"
#include "qmlcodecompletion.h"
#include "qmlhoverhandler.h"
#include "qmlmodelmanager.h"
#include "qmlfilewizard.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/uniqueidmanager.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <texteditor/fontsettings.h>
#include <texteditor/storagesettings.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/textfilewizard.h>
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/completionsupport.h>
#include <help/helpplugin.h>
#include <utils/qtcassert.h>
#include <QtCore/QtPlugin>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtCore/QDir>
#include <QtCore/QCoreApplication>
#include <QtGui/QAction>
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
using namespace QmlJSEditor::Constants;
QmlJSEditorPlugin *QmlJSEditorPlugin::m_instance = 0;
QmlJSEditorPlugin::QmlJSEditorPlugin() :
m_modelManager(0),
m_wizard(0),
m_editor(0),
m_actionHandler(0),
m_completion(0)
{
m_instance = this;
}
QmlJSEditorPlugin::~QmlJSEditorPlugin()
{
removeObject(m_editor);
delete m_actionHandler;
m_instance = 0;
}
bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *error_message)
{
Core::ICore *core = Core::ICore::instance();
if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/qmljseditor/QmlJSEditor.mimetypes.xml"), error_message))
return false;
m_modelManager = new QmlModelManager(this);
addAutoReleasedObject(m_modelManager);
Qml::QmlTypeSystem *typeSystem = new Qml::QmlTypeSystem;
addAutoReleasedObject(typeSystem);
QList<int> context;
context<< core->uniqueIDManager()->uniqueIdentifier(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
m_editor = new QmlJSEditorFactory(this);
addObject(m_editor);
Core::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard);
wizardParameters.setCategory(QLatin1String(Core::Constants::WIZARD_CATEGORY_QT));
wizardParameters.setDisplayCategory(QCoreApplication::translate("Core", Core::Constants::WIZARD_TR_CATEGORY_QT));
wizardParameters.setDescription(tr("Creates a Qt QML file."));
wizardParameters.setDisplayName(tr("Qt QML File"));
wizardParameters.setId(QLatin1String("Q.Qml"));
addAutoReleasedObject(new QmlFileWizard(wizardParameters, core));
m_actionHandler = new TextEditor::TextEditorActionHandler(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
TextEditor::TextEditorActionHandler::Format
| TextEditor::TextEditorActionHandler::UnCommentSelection
| TextEditor::TextEditorActionHandler::UnCollapseAll);
m_actionHandler->initializeActions();
Core::ActionManager *am = core->actionManager();
Core::ActionContainer *contextMenu= am->createMenu(QmlJSEditor::Constants::M_CONTEXT);
Core::Command *cmd = am->command(TextEditor::Constants::AUTO_INDENT_SELECTION);
contextMenu->addAction(cmd);
cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
contextMenu->addAction(cmd);
m_completion = new QmlCodeCompletion(m_modelManager, typeSystem);
addAutoReleasedObject(m_completion);
addAutoReleasedObject(new QmlHoverHandler());
// Restore settings
QSettings *settings = Core::ICore::instance()->settings();
settings->beginGroup(QLatin1String("CppTools")); // ### FIXME:
settings->beginGroup(QLatin1String("Completion"));
const bool caseSensitive = settings->value(QLatin1String("CaseSensitive"), true).toBool();
m_completion->setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
settings->endGroup();
settings->endGroup();
error_message->clear();
return true;
}
void QmlJSEditorPlugin::extensionsInitialized()
{
//
// Explicitly register qml.qch if located in creator directory.
//
// This is only needed for the creator-qml package, were we
// want to ship the documentation without a qt development version.
//
ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
Help::HelpManager *helpManager = pluginManager->getObject<Help::HelpManager>();
Q_ASSERT(helpManager);
const QString qmlHelpFile =
QDir::cleanPath(QCoreApplication::applicationDirPath()
#if defined(Q_OS_MAC)
+ QLatin1String("/../Resources/doc/qml.qch"));
#else
+ QLatin1String("../../share/doc/qtcreator/qml.qch"));
#endif
helpManager->registerDocumentation(QStringList(qmlHelpFile));
}
void QmlJSEditorPlugin::initializeEditor(QmlJSEditor::Internal::QmlJSTextEditor *editor)
{
QTC_ASSERT(m_instance, /**/);
m_actionHandler->setupActions(editor);
TextEditor::TextEditorSettings::instance()->initializeEditor(editor);
// auto completion
connect(editor, SIGNAL(requestAutoCompletion(TextEditor::ITextEditable*, bool)),
TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(TextEditor::ITextEditable*, bool)));
}
Q_EXPORT_PLUGIN(QmlJSEditorPlugin)

View File

@@ -0,0 +1,80 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLJSEDITORPLUGIN_H
#define QMLJSEDITORPLUGIN_H
#include <extensionsystem/iplugin.h>
namespace TextEditor {
class TextEditorActionHandler;
} // namespace TextEditor
namespace QmlJSEditor {
class QmlModelManagerInterface;
class QmlFileWizard;
namespace Internal {
class QmlJSEditorFactory;
class QmlCodeCompletion;
class QmlJSTextEditor;
class QmlJSEditorPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
public:
QmlJSEditorPlugin();
virtual ~QmlJSEditorPlugin();
// IPlugin
bool initialize(const QStringList &arguments, QString *errorMessage = 0);
void extensionsInitialized();
static QmlJSEditorPlugin *instance()
{ return m_instance; }
void initializeEditor(QmlJSTextEditor *editor);
private:
static QmlJSEditorPlugin *m_instance;
QmlModelManagerInterface *m_modelManager;
QmlFileWizard *m_wizard;
QmlJSEditorFactory *m_editor;
TextEditor::TextEditorActionHandler *m_actionHandler;
QmlCodeCompletion *m_completion;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLJSEDITORPLUGIN_H

View File

@@ -0,0 +1,299 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlexpressionundercursor.h"
#include "qmllookupcontext.h"
#include "qmlresolveexpression.h"
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <qmljs/qmltypesystem.h>
#include <QDebug>
using namespace Qml;
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
using namespace QmlJS;
using namespace QmlJS::AST;
QmlLookupContext::QmlLookupContext(const QStack<QmlSymbol *> &scopes,
const QmlDocument::Ptr &doc,
const Snapshot &snapshot,
QmlTypeSystem *typeSystem):
_scopes(scopes),
_doc(doc),
_snapshot(snapshot),
m_typeSystem(typeSystem)
{
Q_ASSERT(typeSystem != 0);
}
static inline int findFirstQmlObjectScope(const QStack<QmlSymbol*> &scopes, int startIdx)
{
if (startIdx < 0 || startIdx >= scopes.size())
return -1;
for (int i = startIdx; i >= 0; --i) {
QmlSymbol *current = scopes.at(i);
if (current->isSymbolFromFile()) {
Node *node = current->asSymbolFromFile()->node();
if (cast<UiObjectBinding*>(node) || cast<UiObjectDefinition*>(node)) {
return i;
}
}
}
return -1;
}
static inline QmlSymbol *resolveParent(const QStack<QmlSymbol*> &scopes)
{
int idx = findFirstQmlObjectScope(scopes, scopes.size() - 1);
if (idx < 1)
return 0;
idx = findFirstQmlObjectScope(scopes, idx - 1);
if (idx < 0)
return 0;
else
return scopes.at(idx);
}
QmlSymbol *QmlLookupContext::resolve(const QString &name)
{
// find element type names
if (QmlSymbol *type = resolveType(name))
return type;
// find ids
const QmlDocument::IdTable ids = _doc->ids();
if (ids.contains(name))
return ids[name];
if (name == "parent") {
return resolveParent(_scopes);
}
// find script methods
// ### TODO
// find properties of the scope object
int scopeObjectIndex = findFirstQmlObjectScope(_scopes, _scopes.size() - 1);
if (scopeObjectIndex != -1)
if (QmlSymbol *propertySymbol = resolveProperty(name, _scopes.at(scopeObjectIndex), _doc->fileName()))
return propertySymbol;
// find properties of the component's root object
if (!_doc->symbols().isEmpty())
if (QmlSymbol *propertySymbol = resolveProperty(name, _doc->symbols()[0], _doc->fileName()))
return propertySymbol;
// component chain
// ### TODO: Might lead to ambiguity.
// context chain
// ### TODO: ?
// global object
// ### TODO
return 0;
}
QmlSymbol *QmlLookupContext::resolveType(const QString &name, const QString &fileName)
{
// TODO: handle import-as.
QmlDocument::Ptr document = _snapshot[fileName];
if (document.isNull())
return 0;
UiProgram *prog = document->program();
if (!prog)
return 0;
UiImportList *imports = prog->imports;
if (!imports)
return 0;
for (UiImportList *iter = imports; iter; iter = iter->next) {
UiImport *import = iter->import;
if (!import)
continue;
// TODO: handle Qt imports
if (!(import->fileName))
continue;
const QString path = import->fileName->asString();
const QMap<QString, QmlDocument::Ptr> importedTypes = _snapshot.componentsDefinedByImportedDocuments(document, path);
if (importedTypes.contains(name)) {
QmlDocument::Ptr importedDoc = importedTypes.value(name);
return importedDoc->symbols().at(0);
}
}
// TODO: handle Qt imports, hack for now:
return resolveBuildinType(name);
}
QmlSymbol *QmlLookupContext::resolveBuildinType(const QString &name)
{
QList<Qml::PackageInfo> packages;
// FIXME:
packages.append(PackageInfo("Qt", 4, 6));
return m_typeSystem->resolve(name, packages);
}
QmlSymbol *QmlLookupContext::resolveProperty(const QString &name, QmlSymbol *scope, const QString &fileName)
{
foreach (QmlSymbol *symbol, scope->members())
if (symbol->isProperty() && symbol->name() == name)
return symbol;
UiQualifiedId *typeName = 0;
if (scope->isSymbolFromFile()) {
Node *ast = scope->asSymbolFromFile()->node();
if (UiObjectBinding *binding = cast<UiObjectBinding*>(ast)) {
typeName = binding->qualifiedTypeNameId;
} else if (UiObjectDefinition *definition = cast<UiObjectDefinition*>(ast)) {
typeName = definition->qualifiedTypeNameId;
} // TODO: extend this to handle (JavaScript) block scopes.
}
if (typeName == 0)
return 0;
QmlSymbol *typeSymbol = resolveType(toString(typeName), fileName);
if (!typeSymbol)
return 0;
if (typeSymbol->isSymbolFromFile()) {
return resolveProperty(name, typeSymbol->asSymbolFromFile(), typeSymbol->asSymbolFromFile()->fileName());
} else if (QmlBuildInSymbol *builtinSymbol = typeSymbol->asBuildInSymbol()) {
foreach (QmlSymbol *member, builtinSymbol->members(true)) {
if (member->isProperty() && member->name() == name)
return member;
}
}
return 0;
}
QString QmlLookupContext::toString(UiQualifiedId *id)
{
QString str;
for (UiQualifiedId *iter = id; iter; iter = iter->next) {
if (!(iter->name))
continue;
str.append(iter->name->asString());
if (iter->next)
str.append('.');
}
return str;
}
QList<QmlSymbol*> QmlLookupContext::visibleSymbolsInScope()
{
QList<QmlSymbol*> result;
if (!_scopes.isEmpty()) {
QmlSymbol *scope = _scopes.top();
// add members defined in this symbol:
result.append(scope->members());
// add the members of the type of this scope (= object):
result.append(expandType(scope));
}
return result;
}
QList<QmlSymbol*> QmlLookupContext::visibleTypes()
{
QList<QmlSymbol*> result;
UiProgram *program = _doc->program();
if (!program)
return result;
for (UiImportList *iter = program->imports; iter; iter = iter->next) {
UiImport *import = iter->import;
if (!import)
continue;
if (!(import->fileName))
continue;
const QString path = import->fileName->asString();
// TODO: handle "import as".
const QMap<QString, QmlDocument::Ptr> types = _snapshot.componentsDefinedByImportedDocuments(_doc, path);
foreach (const QmlDocument::Ptr typeDoc, types) {
result.append(typeDoc->symbols().at(0));
}
}
result.append(m_typeSystem->availableTypes("Qt", 4, 6));
return result;
}
QList<QmlSymbol*> QmlLookupContext::expandType(Qml::QmlSymbol *symbol)
{
if (symbol == 0) {
return QList<QmlSymbol*>();
} else if (QmlBuildInSymbol *buildInSymbol = symbol->asBuildInSymbol()) {
return buildInSymbol->members(true);
} else if (QmlSymbolFromFile *symbolFromFile = symbol->asSymbolFromFile()){
QList<QmlSymbol*> result;
if (QmlSymbol *superTypeSymbol = resolveType(symbolFromFile->name(), symbolFromFile->fileName())) {
result.append(superTypeSymbol->members());
result.append(expandType(superTypeSymbol));
}
return result;
} else {
return QList<QmlSymbol*>();
}
}

View File

@@ -0,0 +1,82 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLLOOKUPCONTEXT_H
#define QMLLOOKUPCONTEXT_H
#include <qmljs/qmltypesystem.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmldocument.h>
#include <qmljs/qmlsymbol.h>
#include <QStack>
namespace QmlJSEditor {
namespace Internal {
class QmlLookupContext
{
public:
QmlLookupContext(const QStack<Qml::QmlSymbol *> &scopes,
const Qml::QmlDocument::Ptr &doc,
const Qml::Snapshot &snapshot,
Qml::QmlTypeSystem *typeSystem);
Qml::QmlSymbol *resolve(const QString &name);
Qml::QmlSymbol *resolveType(const QString &name)
{ return resolveType(name, _doc->fileName()); }
Qml::QmlSymbol *resolveType(QmlJS::AST::UiQualifiedId *name)
{ return resolveType(toString(name), _doc->fileName()); }
Qml::QmlDocument::Ptr document() const
{ return _doc; }
QList<Qml::QmlSymbol*> visibleSymbolsInScope();
QList<Qml::QmlSymbol*> visibleTypes();
QList<Qml::QmlSymbol*> expandType(Qml::QmlSymbol *symbol);
private:
Qml::QmlSymbol *resolveType(const QString &name, const QString &fileName);
Qml::QmlSymbol *resolveProperty(const QString &name, Qml::QmlSymbol *scope, const QString &fileName);
Qml::QmlSymbol *resolveBuildinType(const QString &name);
static QString toString(QmlJS::AST::UiQualifiedId *id);
private:
QStack<Qml::QmlSymbol *> _scopes;
Qml::QmlDocument::Ptr _doc;
Qml::Snapshot _snapshot;
Qml::QmlTypeSystem *m_typeSystem;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLLOOKUPCONTEXT_H

View File

@@ -0,0 +1,165 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include <QFile>
#include <QtConcurrentRun>
#include <qtconcurrent/runextensions.h>
#include <QTextStream>
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/itexteditor.h>
#include "qmljseditorconstants.h"
#include "qmlmodelmanager.h"
#include <QtCore/QMetaType>
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
QmlModelManager::QmlModelManager(QObject *parent):
QmlModelManagerInterface(parent),
m_core(Core::ICore::instance())
{
m_synchronizer.setCancelOnWait(true);
qRegisterMetaType<Qml::QmlDocument::Ptr>("Qml::QmlDocument::Ptr");
connect(this, SIGNAL(documentUpdated(Qml::QmlDocument::Ptr)),
this, SLOT(onDocumentUpdated(Qml::QmlDocument::Ptr)));
}
Qml::Snapshot QmlModelManager::snapshot() const
{
QMutexLocker locker(&m_mutex);
return _snapshot;
}
void QmlModelManager::updateSourceFiles(const QStringList &files)
{
refreshSourceFiles(files);
}
QFuture<void> QmlModelManager::refreshSourceFiles(const QStringList &sourceFiles)
{
if (sourceFiles.isEmpty()) {
return QFuture<void>();
}
const QMap<QString, QString> workingCopy = buildWorkingCopyList();
QFuture<void> result = QtConcurrent::run(&QmlModelManager::parse,
workingCopy, sourceFiles,
this);
if (m_synchronizer.futures().size() > 10) {
QList<QFuture<void> > futures = m_synchronizer.futures();
m_synchronizer.clearFutures();
foreach (QFuture<void> future, futures) {
if (! (future.isFinished() || future.isCanceled()))
m_synchronizer.addFuture(future);
}
}
m_synchronizer.addFuture(result);
if (sourceFiles.count() > 1) {
m_core->progressManager()->addTask(result, tr("Indexing"),
QmlJSEditor::Constants::TASK_INDEX);
}
return result;
}
QMap<QString, QString> QmlModelManager::buildWorkingCopyList()
{
QMap<QString, QString> workingCopy;
Core::EditorManager *editorManager = m_core->editorManager();
foreach (Core::IEditor *editor, editorManager->openedEditors()) {
const QString key = editor->file()->fileName();
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
workingCopy[key] = textEditor->contents();
}
}
return workingCopy;
}
void QmlModelManager::emitDocumentUpdated(Qml::QmlDocument::Ptr doc)
{ emit documentUpdated(doc); }
void QmlModelManager::onDocumentUpdated(Qml::QmlDocument::Ptr doc)
{
QMutexLocker locker(&m_mutex);
_snapshot.insert(doc);
}
void QmlModelManager::parse(QFutureInterface<void> &future,
QMap<QString, QString> workingCopy,
QStringList files,
QmlModelManager *modelManager)
{
future.setProgressRange(0, files.size());
for (int i = 0; i < files.size(); ++i) {
future.setProgressValue(i);
const QString fileName = files.at(i);
QString contents;
if (workingCopy.contains(fileName)) {
contents = workingCopy.value(fileName);
} else {
QFile inFile(fileName);
if (inFile.open(QIODevice::ReadOnly)) {
QTextStream ins(&inFile);
contents = ins.readAll();
inFile.close();
}
}
Qml::QmlDocument::Ptr doc = Qml::QmlDocument::create(fileName);
doc->setSource(contents);
doc->parse();
modelManager->emitDocumentUpdated(doc);
}
future.setProgressValue(files.size());
}

View File

@@ -0,0 +1,88 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLMODELMANAGER_H
#define QMLMODELMANAGER_H
#include "qmlmodelmanagerinterface.h"
#include <qmljs/qmldocument.h>
#include <QFuture>
#include <QFutureSynchronizer>
#include <QMutex>
namespace Core {
class ICore;
}
namespace QmlJSEditor {
namespace Internal {
class QmlModelManager: public QmlModelManagerInterface
{
Q_OBJECT
public:
QmlModelManager(QObject *parent = 0);
virtual Qml::Snapshot snapshot() const;
virtual void updateSourceFiles(const QStringList &files);
void emitDocumentUpdated(Qml::QmlDocument::Ptr doc);
Q_SIGNALS:
void projectPathChanged(const QString &projectPath);
void aboutToRemoveFiles(const QStringList &files);
private Q_SLOTS:
// this should be executed in the GUI thread.
void onDocumentUpdated(Qml::QmlDocument::Ptr doc);
protected:
QFuture<void> refreshSourceFiles(const QStringList &sourceFiles);
QMap<QString, QString> buildWorkingCopyList();
static void parse(QFutureInterface<void> &future,
QMap<QString, QString> workingCopy,
QStringList files,
QmlModelManager *modelManager);
private:
mutable QMutex m_mutex;
Core::ICore *m_core;
Qml::Snapshot _snapshot;
QFutureSynchronizer<void> m_synchronizer;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLMODELMANAGER_H

View File

@@ -0,0 +1,41 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlmodelmanagerinterface.h"
using namespace QmlJSEditor;
QmlModelManagerInterface::QmlModelManagerInterface(QObject *parent):
QObject(parent)
{
}
QmlModelManagerInterface::~QmlModelManagerInterface()
{
}

View File

@@ -0,0 +1,65 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLMODELMANAGERINTERFACE_H
#define QMLMODELMANAGERINTERFACE_H
#include "qmljseditor_global.h"
#include <qmljs/qmldocument.h>
#include <qmljs/qmltypesystem.h>
#include <QObject>
#include <QStringList>
#include <QSharedPointer>
namespace Qml {
class Snapshot;
}
namespace QmlJSEditor {
class QMLJSEDITOR_EXPORT QmlModelManagerInterface: public QObject
{
Q_OBJECT
public:
QmlModelManagerInterface(QObject *parent = 0);
virtual ~QmlModelManagerInterface();
virtual Qml::Snapshot snapshot() const = 0;
virtual void updateSourceFiles(const QStringList &files) = 0;
signals:
void documentUpdated(Qml::QmlDocument::Ptr doc);
};
} // namespace QmlJSEditor
#endif // QMLMODELMANAGERINTERFACE_H

View File

@@ -0,0 +1,132 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmlresolveexpression.h"
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsengine_p.h>
using namespace Qml;
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
using namespace QmlJS;
using namespace QmlJS::AST;
QmlResolveExpression::QmlResolveExpression(const QmlLookupContext &context)
: _context(context), _value(0)
{
}
QmlSymbol *QmlResolveExpression::typeOf(Node *node)
{
QmlSymbol *previousValue = switchValue(0);
Node::accept(node, this);
return switchValue(previousValue);
}
QList<QmlSymbol*> QmlResolveExpression::visibleSymbols(Node *node)
{
QList<QmlSymbol*> result;
QmlSymbol *symbol = typeOf(node);
if (symbol) {
if (symbol->isIdSymbol())
symbol = symbol->asIdSymbol()->parentNode();
result.append(symbol->members());
// TODO: also add symbols from super-types
} else {
result.append(_context.visibleTypes());
}
if (node) {
foreach (QmlIdSymbol *idSymbol, _context.document()->ids().values())
result.append(idSymbol);
}
result.append(_context.visibleSymbolsInScope());
return result;
}
QmlSymbol *QmlResolveExpression::switchValue(QmlSymbol *value)
{
QmlSymbol *previousValue = _value;
_value = value;
return previousValue;
}
bool QmlResolveExpression::visit(IdentifierExpression *ast)
{
const QString name = ast->name->asString();
_value = _context.resolve(name);
return false;
}
static inline bool matches(UiQualifiedId *candidate, const QString &wanted)
{
if (!candidate)
return false;
if (!(candidate->name))
return false;
if (candidate->next)
return false; // TODO: verify this!
return wanted == candidate->name->asString();
}
bool QmlResolveExpression::visit(FieldMemberExpression *ast)
{
const QString memberName = ast->name->asString();
QmlSymbol *base = typeOf(ast->base);
if (!base)
return false;
if (base->isIdSymbol())
base = base->asIdSymbol()->parentNode();
if (!base)
return false;
foreach (QmlSymbol *memberSymbol, base->members())
if (memberSymbol->name() == memberName)
_value = memberSymbol;
return false;
}
bool QmlResolveExpression::visit(QmlJS::AST::UiQualifiedId *ast)
{
_value = _context.resolveType(ast);
return false;
}

View File

@@ -0,0 +1,66 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef QMLRESOLVEEXPRESSION_H
#define QMLRESOLVEEXPRESSION_H
#include "qmllookupcontext.h"
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmlsymbol.h>
namespace QmlJSEditor {
namespace Internal {
class QmlResolveExpression: protected QmlJS::AST::Visitor
{
public:
QmlResolveExpression(const QmlLookupContext &context);
Qml::QmlSymbol *typeOf(QmlJS::AST::Node *node);
QList<Qml::QmlSymbol*> visibleSymbols(QmlJS::AST::Node *node);
protected:
using QmlJS::AST::Visitor::visit;
Qml::QmlSymbol *switchValue(Qml::QmlSymbol *symbol);
virtual bool visit(QmlJS::AST::FieldMemberExpression *ast);
virtual bool visit(QmlJS::AST::IdentifierExpression *ast);
virtual bool visit(QmlJS::AST::UiQualifiedId *ast);
private:
QmlLookupContext _context;
Qml::QmlSymbol *_value;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLRESOLVEEXPRESSION_H