2009-04-22 15:21:04 +02:00
|
|
|
/**************************************************************************
|
|
|
|
**
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
**
|
2010-03-05 11:25:49 +01:00
|
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
2009-04-22 15:21:04 +02:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2009-04-22 15:21:04 +02:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** No Commercial Usage
|
2009-04-22 15:21:04 +02:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** This file contains pre-release code and may not be distributed.
|
|
|
|
** You may use this file in accordance with the terms and conditions
|
|
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
|
|
** this package.
|
2009-04-22 15:21:04 +02:00
|
|
|
**
|
|
|
|
** 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.
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** If you have questions regarding the use of this file, please contact
|
|
|
|
** Nokia at qt-info@nokia.com.
|
2009-04-22 15:21:04 +02:00
|
|
|
**
|
|
|
|
**************************************************************************/
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditor.h"
|
2010-11-02 11:10:27 +01:00
|
|
|
#include "qmljseditoreditable.h"
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditorconstants.h"
|
2010-02-15 12:27:25 +01:00
|
|
|
#include "qmljshighlighter.h"
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditorplugin.h"
|
2010-09-16 12:57:07 +02:00
|
|
|
#include "qmljsquickfix.h"
|
|
|
|
#include "qmloutlinemodel.h"
|
2010-09-24 14:05:34 +02:00
|
|
|
#include "qmljsfindreferences.h"
|
2010-11-02 11:10:27 +01:00
|
|
|
#include "qmljssemantichighlighter.h"
|
2010-11-05 14:27:16 +01:00
|
|
|
#include "qmljsindenter.h"
|
2010-11-08 16:11:26 +01:00
|
|
|
#include "qmljsautocompleter.h"
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-16 11:16:22 +02:00
|
|
|
#include <qmljs/qmljsbind.h>
|
2010-11-16 14:39:54 +01:00
|
|
|
#include <qmljs/qmljsevaluate.h>
|
2010-02-01 16:12:19 +01:00
|
|
|
#include <qmljs/qmljsdocument.h>
|
2010-07-07 13:09:30 +02:00
|
|
|
#include <qmljs/qmljsicontextpane.h>
|
2010-07-16 10:42:26 +02:00
|
|
|
#include <qmljs/qmljslookupcontext.h>
|
2010-11-11 10:05:05 +01:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
2010-01-15 13:39:54 +01:00
|
|
|
#include <qmljs/parser/qmljsastvisitor_p.h>
|
|
|
|
#include <qmljs/parser/qmljsast_p.h>
|
|
|
|
#include <qmljs/parser/qmljsengine_p.h>
|
2009-09-11 14:42:50 +02:00
|
|
|
|
2010-11-11 10:05:05 +01:00
|
|
|
#include <qmljstools/qmljsqtstylecodeformatter.h>
|
|
|
|
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
2010-11-02 16:53:56 +01:00
|
|
|
#include <coreplugin/uniqueidmanager.h>
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/command.h>
|
2010-07-14 12:21:23 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2010-01-15 17:41:12 +01:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
#include <coreplugin/mimedatabase.h>
|
2009-09-04 16:51:11 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <texteditor/basetextdocument.h>
|
|
|
|
#include <texteditor/fontsettings.h>
|
2010-10-29 15:20:10 +02:00
|
|
|
#include <texteditor/tabsettings.h>
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
#include <texteditor/texteditorsettings.h>
|
2010-07-09 14:47:18 +02:00
|
|
|
#include <texteditor/syntaxhighlighter.h>
|
2010-08-05 10:44:50 +02:00
|
|
|
#include <texteditor/refactoroverlay.h>
|
2010-09-29 16:58:30 +02:00
|
|
|
#include <texteditor/tooltip/tooltip.h>
|
2010-03-16 16:51:45 +01:00
|
|
|
#include <qmldesigner/qmldesignerconstants.h>
|
2010-11-11 10:05:05 +01:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2009-12-02 17:59:27 +01:00
|
|
|
#include <utils/changeset.h>
|
2009-05-14 16:37:17 +02:00
|
|
|
#include <utils/uncommentselection.h>
|
|
|
|
|
2010-01-15 17:41:12 +01:00
|
|
|
#include <QtCore/QFileInfo>
|
2010-09-16 12:57:07 +02:00
|
|
|
#include <QtCore/QSignalMapper>
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <QtCore/QTimer>
|
|
|
|
|
|
|
|
#include <QtGui/QMenu>
|
|
|
|
#include <QtGui/QComboBox>
|
2010-07-12 14:45:22 +02:00
|
|
|
#include <QtGui/QHeaderView>
|
2009-05-05 15:33:39 +02:00
|
|
|
#include <QtGui/QInputDialog>
|
2009-05-05 16:18:36 +02:00
|
|
|
#include <QtGui/QMainWindow>
|
2009-07-16 17:34:04 +02:00
|
|
|
#include <QtGui/QToolBar>
|
2010-07-12 14:45:22 +02:00
|
|
|
#include <QtGui/QTreeView>
|
2009-04-22 15:21:04 +02:00
|
|
|
|
|
|
|
enum {
|
2010-09-08 12:52:17 +02:00
|
|
|
UPDATE_DOCUMENT_DEFAULT_INTERVAL = 100,
|
2010-07-12 17:03:49 +02:00
|
|
|
UPDATE_USES_DEFAULT_INTERVAL = 150,
|
2010-12-16 12:05:48 +01:00
|
|
|
UPDATE_OUTLINE_INTERVAL = 500 // msecs after new semantic info has been arrived / cursor has moved
|
2009-04-22 15:21:04 +02:00
|
|
|
};
|
|
|
|
|
2009-06-11 11:22:26 +02:00
|
|
|
using namespace QmlJS;
|
|
|
|
using namespace QmlJS::AST;
|
2010-11-02 11:10:27 +01:00
|
|
|
using namespace QmlJSEditor;
|
2010-01-25 14:18:53 +01:00
|
|
|
using namespace QmlJSEditor::Internal;
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-06-14 16:06:42 +02:00
|
|
|
namespace {
|
|
|
|
|
2009-05-06 17:26:29 +02:00
|
|
|
class FindIdDeclarations: protected Visitor
|
2009-05-04 18:58:36 +02:00
|
|
|
{
|
|
|
|
public:
|
2010-01-26 14:14:01 +01:00
|
|
|
typedef QHash<QString, QList<AST::SourceLocation> > Result;
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2010-01-26 14:14:01 +01:00
|
|
|
Result operator()(Document::Ptr doc)
|
2009-09-16 13:56:06 +02:00
|
|
|
{
|
|
|
|
_ids.clear();
|
|
|
|
_maybeIds.clear();
|
2010-01-26 14:14:01 +01:00
|
|
|
if (doc && doc->qmlProgram())
|
|
|
|
doc->qmlProgram()->accept(this);
|
2009-09-16 13:56:06 +02:00
|
|
|
return _ids;
|
|
|
|
}
|
2009-05-04 18:58:36 +02:00
|
|
|
|
|
|
|
protected:
|
2009-09-16 13:56:06 +02:00
|
|
|
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;
|
|
|
|
}
|
2009-05-04 18:58:36 +02:00
|
|
|
|
|
|
|
private:
|
2009-09-16 13:56:06 +02:00
|
|
|
Result _ids;
|
|
|
|
Result _maybeIds;
|
2009-05-04 18:58:36 +02:00
|
|
|
};
|
|
|
|
|
2009-04-22 15:21:04 +02:00
|
|
|
class FindDeclarations: protected Visitor
|
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
QList<Declaration> _declarations;
|
|
|
|
int _depth;
|
2009-04-22 15:21:04 +02:00
|
|
|
|
|
|
|
public:
|
2009-09-16 13:56:06 +02:00
|
|
|
QList<Declaration> operator()(AST::Node *node)
|
|
|
|
{
|
|
|
|
_depth = -1;
|
|
|
|
_declarations.clear();
|
|
|
|
accept(node);
|
|
|
|
return _declarations;
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
|
|
|
|
protected:
|
2009-09-16 13:56:06 +02:00
|
|
|
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('.');
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
return text;
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
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;
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2010-01-19 10:31:42 +01:00
|
|
|
void init(Declaration *decl, AST::ExpressionNode *expressionNode)
|
|
|
|
{
|
|
|
|
const SourceLocation first = expressionNode->firstSourceLocation();
|
|
|
|
const SourceLocation last = expressionNode->lastSourceLocation();
|
|
|
|
decl->startLine = first.startLine;
|
|
|
|
decl->startColumn = first.startColumn;
|
|
|
|
decl->endLine = last.startLine;
|
|
|
|
decl->endColumn = last.startColumn + last.length;
|
|
|
|
}
|
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
virtual bool visit(AST::UiObjectDefinition *node)
|
|
|
|
{
|
|
|
|
++_depth;
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
Declaration decl;
|
|
|
|
init(&decl, node);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
decl.text.fill(QLatin1Char(' '), _depth);
|
|
|
|
if (node->qualifiedTypeNameId)
|
|
|
|
decl.text.append(asString(node->qualifiedTypeNameId));
|
|
|
|
else
|
|
|
|
decl.text.append(QLatin1Char('?'));
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
_declarations.append(decl);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
return true; // search for more bindings
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
virtual void endVisit(AST::UiObjectDefinition *)
|
|
|
|
{
|
|
|
|
--_depth;
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
virtual bool visit(AST::UiObjectBinding *node)
|
|
|
|
{
|
|
|
|
++_depth;
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
Declaration decl;
|
|
|
|
init(&decl, node);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
decl.text.fill(QLatin1Char(' '), _depth);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
decl.text.append(asString(node->qualifiedId));
|
|
|
|
decl.text.append(QLatin1String(": "));
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
if (node->qualifiedTypeNameId)
|
|
|
|
decl.text.append(asString(node->qualifiedTypeNameId));
|
|
|
|
else
|
|
|
|
decl.text.append(QLatin1Char('?'));
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
_declarations.append(decl);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
return true; // search for more bindings
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
virtual void endVisit(AST::UiObjectBinding *)
|
|
|
|
{
|
|
|
|
--_depth;
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2010-01-19 10:31:42 +01:00
|
|
|
virtual bool visit(AST::UiScriptBinding *)
|
2009-09-16 13:56:06 +02:00
|
|
|
{
|
|
|
|
++_depth;
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2010-01-19 10:31:42 +01:00
|
|
|
#if 0 // ### ignore script bindings for now.
|
2009-09-16 13:56:06 +02:00
|
|
|
Declaration decl;
|
|
|
|
init(&decl, node);
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
decl.text.fill(QLatin1Char(' '), _depth);
|
|
|
|
decl.text.append(asString(node->qualifiedId));
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
_declarations.append(decl);
|
2010-01-19 10:31:42 +01:00
|
|
|
#endif
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
return false; // more more bindings in this subtree.
|
|
|
|
}
|
2009-04-30 17:23:32 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
virtual void endVisit(AST::UiScriptBinding *)
|
|
|
|
{
|
|
|
|
--_depth;
|
|
|
|
}
|
2010-01-19 10:31:42 +01:00
|
|
|
|
|
|
|
virtual bool visit(AST::FunctionExpression *)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::FunctionDeclaration *ast)
|
|
|
|
{
|
|
|
|
if (! ast->name)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Declaration decl;
|
|
|
|
init(&decl, ast);
|
|
|
|
|
|
|
|
decl.text.fill(QLatin1Char(' '), _depth);
|
|
|
|
decl.text += ast->name->asString();
|
|
|
|
|
|
|
|
decl.text += QLatin1Char('(');
|
|
|
|
for (FormalParameterList *it = ast->formals; it; it = it->next) {
|
|
|
|
if (it->name)
|
|
|
|
decl.text += it->name->asString();
|
|
|
|
|
|
|
|
if (it->next)
|
|
|
|
decl.text += QLatin1String(", ");
|
|
|
|
}
|
|
|
|
|
|
|
|
decl.text += QLatin1Char(')');
|
|
|
|
|
|
|
|
_declarations.append(decl);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::VariableDeclaration *ast)
|
|
|
|
{
|
|
|
|
if (! ast->name)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Declaration decl;
|
|
|
|
decl.text.fill(QLatin1Char(' '), _depth);
|
|
|
|
decl.text += ast->name->asString();
|
|
|
|
|
|
|
|
const SourceLocation first = ast->identifierToken;
|
|
|
|
decl.startLine = first.startLine;
|
|
|
|
decl.startColumn = first.startColumn;
|
|
|
|
decl.endLine = first.startLine;
|
|
|
|
decl.endColumn = first.startColumn + first.length;
|
|
|
|
|
|
|
|
_declarations.append(decl);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
};
|
|
|
|
|
2010-01-25 14:18:53 +01:00
|
|
|
class CreateRanges: protected AST::Visitor
|
|
|
|
{
|
|
|
|
QTextDocument *_textDocument;
|
|
|
|
QList<Range> _ranges;
|
|
|
|
|
|
|
|
public:
|
|
|
|
QList<Range> operator()(QTextDocument *textDocument, Document::Ptr doc)
|
|
|
|
{
|
|
|
|
_textDocument = textDocument;
|
|
|
|
_ranges.clear();
|
2010-02-08 21:37:59 +01:00
|
|
|
if (doc && doc->ast() != 0)
|
|
|
|
doc->ast()->accept(this);
|
2010-01-25 14:18:53 +01:00
|
|
|
return _ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
using AST::Visitor::visit;
|
|
|
|
|
|
|
|
virtual bool visit(AST::UiObjectBinding *ast)
|
|
|
|
{
|
2010-10-27 11:11:36 +02:00
|
|
|
if (ast->initializer && ast->initializer->lbraceToken.length)
|
2010-01-28 16:29:45 +01:00
|
|
|
_ranges.append(createRange(ast, ast->initializer));
|
2010-01-25 14:18:53 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::UiObjectDefinition *ast)
|
|
|
|
{
|
2010-10-27 11:11:36 +02:00
|
|
|
if (ast->initializer && ast->initializer->lbraceToken.length)
|
2010-01-28 16:29:45 +01:00
|
|
|
_ranges.append(createRange(ast, ast->initializer));
|
2010-01-25 14:18:53 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-02-02 16:36:14 +01:00
|
|
|
virtual bool visit(AST::FunctionExpression *ast)
|
|
|
|
{
|
|
|
|
_ranges.append(createRange(ast));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::FunctionDeclaration *ast)
|
|
|
|
{
|
|
|
|
_ranges.append(createRange(ast));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-01-28 16:29:45 +01:00
|
|
|
Range createRange(AST::UiObjectMember *member, AST::UiObjectInitializer *ast)
|
2010-01-25 14:18:53 +01:00
|
|
|
{
|
|
|
|
Range range;
|
|
|
|
|
2010-01-28 16:29:45 +01:00
|
|
|
range.ast = member;
|
2010-01-25 14:18:53 +01:00
|
|
|
|
|
|
|
range.begin = QTextCursor(_textDocument);
|
2010-06-14 14:52:43 +02:00
|
|
|
range.begin.setPosition(member->firstSourceLocation().begin());
|
2010-01-25 14:18:53 +01:00
|
|
|
|
|
|
|
range.end = QTextCursor(_textDocument);
|
2010-01-28 16:29:45 +01:00
|
|
|
range.end.setPosition(ast->rbraceToken.end());
|
2010-01-25 14:18:53 +01:00
|
|
|
return range;
|
|
|
|
}
|
2010-02-02 16:36:14 +01:00
|
|
|
|
|
|
|
Range createRange(AST::FunctionExpression *ast)
|
|
|
|
{
|
|
|
|
Range range;
|
|
|
|
|
|
|
|
range.ast = ast;
|
|
|
|
|
|
|
|
range.begin = QTextCursor(_textDocument);
|
|
|
|
range.begin.setPosition(ast->lbraceToken.begin());
|
|
|
|
|
|
|
|
range.end = QTextCursor(_textDocument);
|
|
|
|
range.end.setPosition(ast->rbraceToken.end());
|
2010-02-08 21:37:59 +01:00
|
|
|
|
2010-02-02 16:36:14 +01:00
|
|
|
return range;
|
|
|
|
}
|
|
|
|
|
2010-01-25 14:18:53 +01:00
|
|
|
};
|
|
|
|
|
2010-02-01 16:12:19 +01:00
|
|
|
|
|
|
|
class CollectASTNodes: protected AST::Visitor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QList<AST::UiQualifiedId *> qualifiedIds;
|
|
|
|
QList<AST::IdentifierExpression *> identifiers;
|
|
|
|
QList<AST::FieldMemberExpression *> fieldMembers;
|
|
|
|
|
|
|
|
void accept(AST::Node *node)
|
|
|
|
{
|
|
|
|
if (node)
|
|
|
|
node->accept(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
using AST::Visitor::visit;
|
|
|
|
|
|
|
|
virtual bool visit(AST::UiQualifiedId *ast)
|
|
|
|
{
|
|
|
|
qualifiedIds.append(ast);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::IdentifierExpression *ast)
|
|
|
|
{
|
|
|
|
identifiers.append(ast);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool visit(AST::FieldMemberExpression *ast)
|
|
|
|
{
|
|
|
|
fieldMembers.append(ast);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-01-25 14:18:53 +01:00
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
|
2010-02-02 16:36:14 +01:00
|
|
|
AST::Node *SemanticInfo::declaringMember(int cursorPosition) const
|
2010-02-01 16:12:19 +01:00
|
|
|
{
|
2010-02-02 16:36:14 +01:00
|
|
|
AST::Node *declaringMember = 0;
|
2010-02-01 16:12:19 +01:00
|
|
|
|
|
|
|
for (int i = ranges.size() - 1; i != -1; --i) {
|
|
|
|
const Range &range = ranges.at(i);
|
|
|
|
|
|
|
|
if (range.begin.isNull() || range.end.isNull()) {
|
|
|
|
continue;
|
|
|
|
} else if (cursorPosition >= range.begin.position() && cursorPosition <= range.end.position()) {
|
|
|
|
declaringMember = range.ast;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return declaringMember;
|
|
|
|
}
|
|
|
|
|
2010-07-20 14:59:38 +02:00
|
|
|
QmlJS::AST::Node *SemanticInfo::declaringMemberNoProperties(int cursorPosition) const
|
|
|
|
{
|
|
|
|
AST::Node *node = declaringMember(cursorPosition);
|
|
|
|
|
|
|
|
if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition*>(node)) {
|
|
|
|
QString name = objectDefinition->qualifiedTypeNameId->name->asString();
|
|
|
|
if (!name.isNull() && name.at(0).isLower()) {
|
|
|
|
QList<AST::Node *> path = astPath(cursorPosition);
|
|
|
|
if (path.size() > 1)
|
|
|
|
return path.at(path.size() - 2);
|
|
|
|
} else if (name.contains("GradientStop")) {
|
|
|
|
QList<AST::Node *> path = astPath(cursorPosition);
|
|
|
|
if (path.size() > 2)
|
|
|
|
return path.at(path.size() - 3);
|
|
|
|
}
|
|
|
|
} else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(node)) {
|
|
|
|
QString name = objectBinding->qualifiedTypeNameId->name->asString();
|
|
|
|
if (name.contains("Gradient")) {
|
|
|
|
QList<AST::Node *> path = astPath(cursorPosition);
|
|
|
|
if (path.size() > 1)
|
|
|
|
return path.at(path.size() - 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2010-02-19 10:16:33 +01:00
|
|
|
QList<AST::Node *> SemanticInfo::astPath(int cursorPosition) const
|
|
|
|
{
|
|
|
|
QList<AST::Node *> path;
|
|
|
|
|
|
|
|
foreach (const Range &range, ranges) {
|
|
|
|
if (range.begin.isNull() || range.end.isNull()) {
|
|
|
|
continue;
|
|
|
|
} else if (cursorPosition >= range.begin.position() && cursorPosition <= range.end.position()) {
|
|
|
|
path += range.ast;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2010-08-26 10:50:00 +02:00
|
|
|
LookupContext::Ptr SemanticInfo::lookupContext(const QList<QmlJS::AST::Node *> &path) const
|
|
|
|
{
|
2010-09-08 10:11:44 +02:00
|
|
|
Q_ASSERT(! m_context.isNull());
|
|
|
|
|
|
|
|
if (m_context.isNull())
|
|
|
|
return LookupContext::create(document, snapshot, path);
|
|
|
|
|
2010-08-26 10:50:00 +02:00
|
|
|
return LookupContext::create(document, snapshot, *m_context, path);
|
|
|
|
}
|
|
|
|
|
2010-07-16 11:16:22 +02:00
|
|
|
static bool importContainsCursor(UiImport *importAst, unsigned cursorPosition)
|
|
|
|
{
|
|
|
|
return cursorPosition >= importAst->firstSourceLocation().begin()
|
|
|
|
&& cursorPosition <= importAst->lastSourceLocation().end();
|
|
|
|
}
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
AST::Node *SemanticInfo::nodeUnderCursor(int pos) const
|
|
|
|
{
|
|
|
|
if (! document)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const unsigned cursorPosition = pos;
|
|
|
|
|
2010-09-16 15:29:37 +02:00
|
|
|
foreach (const Interpreter::ImportInfo &import, document->bind()->imports()) {
|
|
|
|
if (importContainsCursor(import.ast(), cursorPosition))
|
|
|
|
return import.ast();
|
2010-07-16 11:16:22 +02:00
|
|
|
}
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
CollectASTNodes nodes;
|
|
|
|
nodes.accept(document->ast());
|
|
|
|
|
|
|
|
foreach (AST::UiQualifiedId *q, nodes.qualifiedIds) {
|
|
|
|
if (cursorPosition >= q->identifierToken.begin()) {
|
|
|
|
for (AST::UiQualifiedId *tail = q; tail; tail = tail->next) {
|
|
|
|
if (! tail->next && cursorPosition <= tail->identifierToken.end())
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (AST::IdentifierExpression *id, nodes.identifiers) {
|
|
|
|
if (cursorPosition >= id->identifierToken.begin() && cursorPosition <= id->identifierToken.end())
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (AST::FieldMemberExpression *mem, nodes.fieldMembers) {
|
|
|
|
if (mem->name && cursorPosition >= mem->identifierToken.begin() && cursorPosition <= mem->identifierToken.end())
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-09-08 10:11:44 +02:00
|
|
|
bool SemanticInfo::isValid() const
|
|
|
|
{
|
|
|
|
if (document && m_context)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-26 14:14:01 +01:00
|
|
|
int SemanticInfo::revision() const
|
|
|
|
{
|
|
|
|
if (document)
|
2010-07-07 17:52:02 +02:00
|
|
|
return document->editorRevision();
|
2010-01-26 14:14:01 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
|
2009-09-16 13:56:06 +02:00
|
|
|
TextEditor::BaseTextEditor(parent),
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo(0),
|
2010-07-12 14:45:22 +02:00
|
|
|
m_outlineModel(new QmlOutlineModel(this)),
|
2010-07-07 13:09:30 +02:00
|
|
|
m_modelManager(0),
|
2010-08-24 15:13:50 +02:00
|
|
|
m_contextPane(0),
|
2010-09-24 14:05:34 +02:00
|
|
|
m_updateSelectedElements(false),
|
2010-09-29 19:35:25 +02:00
|
|
|
m_findReferences(new FindReferences(this))
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-11-02 11:10:27 +01:00
|
|
|
qRegisterMetaType<QmlJSEditor::SemanticInfo>("QmlJSEditor::SemanticInfo");
|
2010-02-16 10:36:09 +01:00
|
|
|
|
|
|
|
m_semanticHighlighter = new SemanticHighlighter(this);
|
|
|
|
m_semanticHighlighter->start();
|
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
setMarksVisible(true);
|
|
|
|
setCodeFoldingSupported(true);
|
2010-11-05 14:27:16 +01:00
|
|
|
setIndenter(new Indenter);
|
2010-11-08 16:11:26 +01:00
|
|
|
setAutoCompleter(new AutoCompleter);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
m_updateDocumentTimer = new QTimer(this);
|
|
|
|
m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
|
|
|
|
m_updateDocumentTimer->setSingleShot(true);
|
|
|
|
connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow()));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-01-14 16:30:48 +01:00
|
|
|
m_updateUsesTimer = new QTimer(this);
|
|
|
|
m_updateUsesTimer->setInterval(UPDATE_USES_DEFAULT_INTERVAL);
|
|
|
|
m_updateUsesTimer->setSingleShot(true);
|
|
|
|
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
|
|
|
|
|
2010-04-01 15:47:52 +02:00
|
|
|
m_semanticRehighlightTimer = new QTimer(this);
|
|
|
|
m_semanticRehighlightTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
|
|
|
|
m_semanticRehighlightTimer->setSingleShot(true);
|
2010-04-06 11:57:08 +02:00
|
|
|
connect(m_semanticRehighlightTimer, SIGNAL(timeout()), this, SLOT(forceSemanticRehighlight()));
|
2010-04-01 15:47:52 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(updateDocument()));
|
2010-08-30 12:10:44 +02:00
|
|
|
|
2010-01-14 16:30:48 +01:00
|
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
|
2010-08-30 12:10:44 +02:00
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-13 11:18:42 +02:00
|
|
|
m_updateOutlineTimer = new QTimer(this);
|
|
|
|
m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
m_updateOutlineTimer->setSingleShot(true);
|
|
|
|
connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
|
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
m_updateOutlineIndexTimer = new QTimer(this);
|
|
|
|
m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
m_updateOutlineIndexTimer->setSingleShot(true);
|
|
|
|
connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
|
2010-07-12 17:03:49 +02:00
|
|
|
|
2010-08-27 10:32:04 +02:00
|
|
|
m_cursorPositionTimer = new QTimer(this);
|
2010-09-08 16:34:49 +02:00
|
|
|
m_cursorPositionTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
|
2010-08-27 10:32:04 +02:00
|
|
|
m_cursorPositionTimer->setSingleShot(true);
|
|
|
|
connect(m_cursorPositionTimer, SIGNAL(timeout()), this, SLOT(updateCursorPositionNow()));
|
2010-08-05 10:44:50 +02:00
|
|
|
|
2010-02-15 12:27:25 +01:00
|
|
|
baseTextDocument()->setSyntaxHighlighter(new Highlighter(document()));
|
2010-12-20 18:10:50 +01:00
|
|
|
baseTextDocument()->setCodec(QTextCodec::codecForName("UTF-8")); // qml files are defined to be utf-8
|
2009-09-04 16:51:11 +02:00
|
|
|
|
2010-02-15 13:49:00 +01:00
|
|
|
m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<ModelManagerInterface>();
|
2010-07-07 13:09:30 +02:00
|
|
|
m_contextPane = ExtensionSystem::PluginManager::instance()->getObject<QmlJS::IContextPane>();
|
2010-08-13 13:59:41 +02:00
|
|
|
|
|
|
|
|
|
|
|
if (m_contextPane) {
|
2010-07-07 13:09:30 +02:00
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged()));
|
2010-08-13 13:59:41 +02:00
|
|
|
connect(m_contextPane, SIGNAL(closed()), this, SLOT(showTextMarker()));
|
|
|
|
}
|
2010-07-13 11:25:45 +02:00
|
|
|
m_oldCursorPosition = -1;
|
2009-09-04 16:51:11 +02:00
|
|
|
|
|
|
|
if (m_modelManager) {
|
2010-03-16 16:34:33 +01:00
|
|
|
m_semanticHighlighter->setModelManager(m_modelManager);
|
2010-01-18 16:15:23 +01:00
|
|
|
connect(m_modelManager, SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
|
|
|
|
this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
|
2010-10-15 10:09:40 +02:00
|
|
|
connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
|
|
|
|
this, SLOT(forceSemanticRehighlight()));
|
2010-04-16 12:42:12 +02:00
|
|
|
connect(this->document(), SIGNAL(modificationChanged(bool)), this, SLOT(modificationChanged(bool)));
|
2009-09-04 16:51:11 +02:00
|
|
|
}
|
2010-02-16 10:36:09 +01:00
|
|
|
|
2010-11-02 11:10:27 +01:00
|
|
|
connect(m_semanticHighlighter, SIGNAL(changed(QmlJSEditor::SemanticInfo)),
|
|
|
|
this, SLOT(updateSemanticInfo(QmlJSEditor::SemanticInfo)));
|
2010-03-17 18:41:14 +01:00
|
|
|
|
2010-11-01 16:29:45 +01:00
|
|
|
connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
|
|
|
|
SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));
|
2010-08-05 10:44:50 +02:00
|
|
|
|
2010-07-07 13:56:39 +02:00
|
|
|
setRequestMarkEnabled(true);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
QmlJSTextEditor::~QmlJSTextEditor()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-02-16 10:36:09 +01:00
|
|
|
m_semanticHighlighter->abort();
|
|
|
|
m_semanticHighlighter->wait();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-02-02 15:01:42 +01:00
|
|
|
SemanticInfo QmlJSTextEditor::semanticInfo() const
|
|
|
|
{
|
|
|
|
return m_semanticInfo;
|
|
|
|
}
|
|
|
|
|
2010-07-13 10:45:20 +02:00
|
|
|
int QmlJSTextEditor::editorRevision() const
|
2010-02-02 15:01:42 +01:00
|
|
|
{
|
|
|
|
return document()->revision();
|
|
|
|
}
|
|
|
|
|
2010-06-03 15:49:29 +02:00
|
|
|
bool QmlJSTextEditor::isOutdated() const
|
|
|
|
{
|
2010-07-13 10:45:20 +02:00
|
|
|
if (m_semanticInfo.revision() != editorRevision())
|
2010-06-03 15:49:29 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
QmlOutlineModel *QmlJSTextEditor::outlineModel() const
|
|
|
|
{
|
|
|
|
return m_outlineModel;
|
|
|
|
}
|
|
|
|
|
2010-07-12 16:40:15 +02:00
|
|
|
QModelIndex QmlJSTextEditor::outlineModelIndex()
|
2010-07-12 14:45:22 +02:00
|
|
|
{
|
2010-07-12 16:40:15 +02:00
|
|
|
if (!m_outlineModelIndex.isValid()) {
|
|
|
|
m_outlineModelIndex = indexForPosition(position());
|
|
|
|
emit outlineModelIndexChanged(m_outlineModelIndex);
|
|
|
|
}
|
2010-07-12 14:45:22 +02:00
|
|
|
return m_outlineModelIndex;
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
Core::IEditor *QmlJSEditorEditable::duplicate(QWidget *parent)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-01-15 17:20:03 +01:00
|
|
|
QmlJSTextEditor *newEditor = new QmlJSTextEditor(parent);
|
2009-09-16 13:56:06 +02:00
|
|
|
newEditor->duplicateFrom(editor());
|
2010-01-15 17:20:03 +01:00
|
|
|
QmlJSEditorPlugin::instance()->initializeEditor(newEditor);
|
2009-09-16 13:56:06 +02:00
|
|
|
return newEditor->editableInterface();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
QString QmlJSEditorEditable::id() const
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-01-15 17:20:03 +01:00
|
|
|
return QLatin1String(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:41:12 +01:00
|
|
|
bool QmlJSEditorEditable::open(const QString &fileName)
|
|
|
|
{
|
|
|
|
bool b = TextEditor::BaseTextEditorEditable::open(fileName);
|
|
|
|
editor()->setMimeType(Core::ICore::instance()->mimeDatabase()->findByFile(QFileInfo(fileName)).type());
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2010-06-25 12:56:16 +02:00
|
|
|
Core::Context QmlJSEditorEditable::context() const
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
return m_context;
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::updateDocument()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
m_updateDocumentTimer->start(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::updateDocumentNow()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
// ### move in the parser thread.
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
m_updateDocumentTimer->stop();
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
const QString fileName = file()->fileName();
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-04-16 13:08:59 +02:00
|
|
|
m_modelManager->updateSourceFiles(QStringList() << fileName, false);
|
2009-09-04 16:51:11 +02:00
|
|
|
}
|
|
|
|
|
2010-02-23 16:11:58 +01:00
|
|
|
static void appendExtraSelectionsForMessages(
|
|
|
|
QList<QTextEdit::ExtraSelection> *selections,
|
|
|
|
const QList<DiagnosticMessage> &messages,
|
|
|
|
const QTextDocument *document)
|
|
|
|
{
|
|
|
|
foreach (const DiagnosticMessage &d, messages) {
|
|
|
|
const int line = d.loc.startLine;
|
|
|
|
const int column = qMax(1U, d.loc.startColumn);
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
QTextCursor c(document->findBlockByNumber(line - 1));
|
|
|
|
sel.cursor = c;
|
|
|
|
|
|
|
|
sel.cursor.setPosition(c.position() + column - 1);
|
|
|
|
|
|
|
|
if (d.loc.length == 0) {
|
|
|
|
if (sel.cursor.atBlockEnd())
|
|
|
|
sel.cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
|
|
|
else
|
|
|
|
sel.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
} else {
|
|
|
|
sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d.loc.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d.isWarning())
|
|
|
|
sel.format.setUnderlineColor(Qt::darkYellow);
|
|
|
|
else
|
|
|
|
sel.format.setUnderlineColor(Qt::red);
|
|
|
|
|
|
|
|
sel.format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
|
|
sel.format.setToolTip(d.message);
|
|
|
|
|
|
|
|
selections->append(sel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
void QmlJSTextEditor::onDocumentUpdated(QmlJS::Document::Ptr doc)
|
2009-09-04 16:51:11 +02:00
|
|
|
{
|
2010-03-31 14:55:05 +02:00
|
|
|
if (file()->fileName() != doc->fileName()
|
2010-07-07 17:52:02 +02:00
|
|
|
|| doc->editorRevision() != document()->revision()) {
|
2010-01-25 14:18:53 +01:00
|
|
|
return;
|
|
|
|
}
|
2009-04-30 16:57:27 +02:00
|
|
|
|
2010-01-26 12:09:07 +01:00
|
|
|
if (doc->ast()) {
|
|
|
|
// got a correctly parsed (or recovered) file.
|
|
|
|
|
2010-11-02 11:10:27 +01:00
|
|
|
const SemanticHighlighterSource source = currentSource(/*force = */ true);
|
2010-02-16 10:36:09 +01:00
|
|
|
m_semanticHighlighter->rehighlight(source);
|
2010-02-23 16:11:58 +01:00
|
|
|
} else {
|
2010-03-31 14:55:05 +02:00
|
|
|
// show parsing errors
|
2010-02-23 16:11:58 +01:00
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
|
|
|
|
setExtraSelections(CodeWarningsSelection, selections);
|
2009-09-04 16:51:11 +02:00
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-04-16 12:42:12 +02:00
|
|
|
void QmlJSTextEditor::modificationChanged(bool changed)
|
|
|
|
{
|
|
|
|
if (!changed && m_modelManager)
|
|
|
|
m_modelManager->fileChangedOnDisk(file()->fileName());
|
|
|
|
}
|
|
|
|
|
2010-07-13 11:25:45 +02:00
|
|
|
void QmlJSTextEditor::jumpToOutlineElement(int /*index*/)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-13 11:24:16 +02:00
|
|
|
QModelIndex index = m_outlineCombo->view()->currentIndex();
|
2010-08-25 13:21:38 +02:00
|
|
|
AST::SourceLocation location = m_outlineModel->sourceLocation(index);
|
|
|
|
|
|
|
|
if (!location.isValid())
|
|
|
|
return;
|
2010-07-12 14:45:22 +02:00
|
|
|
|
2010-07-14 12:21:23 +02:00
|
|
|
Core::EditorManager *editorManager = Core::EditorManager::instance();
|
|
|
|
editorManager->cutForwardNavigationHistory();
|
|
|
|
editorManager->addCurrentPositionToNavigationHistory();
|
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
cursor.setPosition(location.offset);
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
|
|
|
setFocus();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-07-13 11:18:42 +02:00
|
|
|
void QmlJSTextEditor::updateOutlineNow()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-19 11:39:41 +02:00
|
|
|
if (!m_semanticInfo.document)
|
2010-07-13 11:18:42 +02:00
|
|
|
return;
|
|
|
|
|
2010-07-19 11:39:41 +02:00
|
|
|
if (m_semanticInfo.document->editorRevision() != editorRevision()) {
|
2010-07-13 11:18:42 +02:00
|
|
|
m_updateOutlineTimer->start();
|
|
|
|
return;
|
2009-09-16 13:56:06 +02:00
|
|
|
}
|
2010-07-13 11:18:42 +02:00
|
|
|
|
2010-07-19 11:39:41 +02:00
|
|
|
m_outlineModel->update(m_semanticInfo);
|
2010-07-14 13:01:39 +02:00
|
|
|
|
|
|
|
QTreeView *treeView = static_cast<QTreeView*>(m_outlineCombo->view());
|
|
|
|
treeView->expandAll();
|
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
updateOutlineIndexNow();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
void QmlJSTextEditor::updateOutlineIndexNow()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-13 11:24:16 +02:00
|
|
|
if (m_updateOutlineTimer->isActive())
|
2010-07-13 11:25:45 +02:00
|
|
|
return; // updateOutlineNow will call this function soon anyway
|
2010-07-13 11:24:16 +02:00
|
|
|
|
2010-07-13 11:18:42 +02:00
|
|
|
if (!m_outlineModel->document())
|
2010-07-12 17:03:49 +02:00
|
|
|
return;
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-13 11:18:42 +02:00
|
|
|
if (m_outlineModel->document()->editorRevision() != editorRevision()) {
|
2010-07-13 11:24:16 +02:00
|
|
|
m_updateOutlineIndexTimer->start();
|
2010-07-12 17:03:49 +02:00
|
|
|
return;
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 16:40:15 +02:00
|
|
|
m_outlineModelIndex = QModelIndex(); // invalidate
|
|
|
|
QModelIndex comboIndex = outlineModelIndex();
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
if (comboIndex.isValid()) {
|
2010-07-13 11:24:16 +02:00
|
|
|
bool blocked = m_outlineCombo->blockSignals(true);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
// There is no direct way to select a non-root item
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setRootModelIndex(comboIndex.parent());
|
|
|
|
m_outlineCombo->setCurrentIndex(comboIndex.row());
|
|
|
|
m_outlineCombo->setRootModelIndex(QModelIndex());
|
2010-07-12 14:45:22 +02:00
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->blockSignals(blocked);
|
2009-09-16 13:56:06 +02:00
|
|
|
}
|
2010-01-14 16:30:48 +01:00
|
|
|
}
|
|
|
|
|
2010-10-08 13:36:08 +02:00
|
|
|
static UiQualifiedId *qualifiedTypeNameId(Node *m)
|
2010-08-05 10:44:50 +02:00
|
|
|
{
|
|
|
|
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(m))
|
|
|
|
return def->qualifiedTypeNameId;
|
|
|
|
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(m))
|
|
|
|
return binding->qualifiedTypeNameId;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlJSTextEditor::updateCursorPositionNow()
|
|
|
|
{
|
2010-10-04 14:32:50 +02:00
|
|
|
if (m_contextPane && document() && semanticInfo().isValid()
|
|
|
|
&& document()->revision() == semanticInfo().document->editorRevision())
|
2010-08-27 10:37:15 +02:00
|
|
|
{
|
2010-08-05 10:44:50 +02:00
|
|
|
Node *oldNode = m_semanticInfo.declaringMemberNoProperties(m_oldCursorPosition);
|
|
|
|
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
|
2010-08-13 17:20:33 +02:00
|
|
|
if (oldNode != newNode && m_oldCursorPosition != -1)
|
2010-10-21 15:41:58 +02:00
|
|
|
m_contextPane->apply(editableInterface(), semanticInfo().document, LookupContext::Ptr(),newNode, false);
|
|
|
|
if (m_contextPane->isAvailable(editableInterface(), semanticInfo().document, newNode) &&
|
2010-08-05 15:35:52 +02:00
|
|
|
!m_contextPane->widget()->isVisible()) {
|
2010-11-01 16:29:45 +01:00
|
|
|
QList<TextEditor::RefactorMarker> markers;
|
2010-08-05 10:44:50 +02:00
|
|
|
if (UiObjectMember *m = newNode->uiObjectMemberCast()) {
|
2010-09-08 16:34:49 +02:00
|
|
|
const int start = qualifiedTypeNameId(m)->identifierToken.begin();
|
2010-08-05 10:44:50 +02:00
|
|
|
for (UiQualifiedId *q = qualifiedTypeNameId(m); q; q = q->next) {
|
|
|
|
if (! q->next) {
|
|
|
|
const int end = q->identifierToken.end();
|
2010-09-08 16:34:49 +02:00
|
|
|
if (position() >= start && position() <= end) {
|
2010-11-01 16:29:45 +01:00
|
|
|
TextEditor::RefactorMarker marker;
|
2010-09-08 16:34:49 +02:00
|
|
|
QTextCursor tc(document());
|
|
|
|
tc.setPosition(end);
|
|
|
|
marker.cursor = tc;
|
|
|
|
marker.tooltip = tr("Show Qt Quick ToolBar");
|
|
|
|
markers.append(marker);
|
|
|
|
} else {
|
2010-11-01 16:29:45 +01:00
|
|
|
QList<TextEditor::RefactorMarker> markers;
|
2010-09-08 16:34:49 +02:00
|
|
|
setRefactorMarkers(markers);
|
|
|
|
}
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setRefactorMarkers(markers);
|
2010-09-01 11:54:35 +02:00
|
|
|
} else if (oldNode != newNode) {
|
2010-11-01 16:29:45 +01:00
|
|
|
QList<TextEditor::RefactorMarker> markers;
|
2010-08-13 13:59:41 +02:00
|
|
|
setRefactorMarkers(markers);
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
m_oldCursorPosition = position();
|
2010-08-27 10:37:15 +02:00
|
|
|
|
|
|
|
setSelectedElements();
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-13 13:59:41 +02:00
|
|
|
void QmlJSTextEditor::showTextMarker()
|
|
|
|
{
|
|
|
|
m_oldCursorPosition = -1;
|
|
|
|
updateCursorPositionNow();
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::updateUses()
|
2010-01-14 16:30:48 +01:00
|
|
|
{
|
|
|
|
m_updateUsesTimer->start();
|
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
bool QmlJSTextEditor::updateSelectedElements() const
|
|
|
|
{
|
|
|
|
return m_updateSelectedElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlJSTextEditor::setUpdateSelectedElements(bool value)
|
|
|
|
{
|
|
|
|
m_updateSelectedElements = value;
|
|
|
|
}
|
|
|
|
|
2010-09-30 14:25:20 +02:00
|
|
|
void QmlJSTextEditor::renameId(const QString &oldId, const QString &newId)
|
|
|
|
{
|
|
|
|
Utils::ChangeSet changeSet;
|
|
|
|
|
|
|
|
foreach (const AST::SourceLocation &loc, m_semanticInfo.idLocations.value(oldId)) {
|
|
|
|
changeSet.replace(loc.begin(), loc.end(), newId);
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
changeSet.apply(&tc);
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::updateUsesNow()
|
2010-01-14 16:30:48 +01:00
|
|
|
{
|
2010-01-26 14:14:01 +01:00
|
|
|
if (document()->revision() != m_semanticInfo.revision()) {
|
2010-01-14 16:30:48 +01:00
|
|
|
updateUses();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_updateUsesTimer->stop();
|
2009-05-04 18:58:36 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
2010-01-26 14:14:01 +01:00
|
|
|
foreach (const AST::SourceLocation &loc, m_semanticInfo.idLocations.value(wordUnderCursor())) {
|
2009-09-16 13:56:06 +02:00
|
|
|
if (! loc.isValid())
|
|
|
|
continue;
|
2009-05-05 10:46:58 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2010-01-14 16:30:48 +01:00
|
|
|
sel.format = m_occurrencesFormat;
|
2009-09-16 13:56:06 +02:00
|
|
|
sel.cursor = textCursor();
|
|
|
|
sel.cursor.setPosition(loc.begin());
|
|
|
|
sel.cursor.setPosition(loc.end(), QTextCursor::KeepAnchor);
|
|
|
|
selections.append(sel);
|
|
|
|
}
|
2009-05-04 18:58:36 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class SelectedElement: protected Visitor
|
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
unsigned m_cursorPositionStart;
|
|
|
|
unsigned m_cursorPositionEnd;
|
|
|
|
QList<UiObjectMember *> m_selectedMembers;
|
|
|
|
LookupContext::Ptr m_lookupContext;
|
2010-07-08 11:34:51 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
SelectedElement()
|
2010-07-27 11:57:06 +02:00
|
|
|
: m_cursorPositionStart(0), m_cursorPositionEnd(0) {}
|
2010-07-08 11:34:51 +02:00
|
|
|
|
2010-08-26 10:50:00 +02:00
|
|
|
QList<UiObjectMember *> operator()(LookupContext::Ptr lookupContext, unsigned startPosition, unsigned endPosition)
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2010-08-26 10:50:00 +02:00
|
|
|
m_lookupContext = lookupContext;
|
2010-07-27 11:57:06 +02:00
|
|
|
m_cursorPositionStart = startPosition;
|
|
|
|
m_cursorPositionEnd = endPosition;
|
|
|
|
m_selectedMembers.clear();
|
2010-08-26 10:50:00 +02:00
|
|
|
Node::accept(lookupContext->document()->qmlProgram(), this);
|
2010-07-27 11:57:06 +02:00
|
|
|
return m_selectedMembers;
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2010-07-13 16:13:26 +02:00
|
|
|
|
2010-07-27 11:57:06 +02:00
|
|
|
bool isSelectable(UiObjectMember *member) const
|
2010-07-13 16:13:26 +02:00
|
|
|
{
|
|
|
|
UiQualifiedId *id = 0;
|
|
|
|
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member))
|
|
|
|
id = def->qualifiedTypeNameId;
|
|
|
|
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(member))
|
|
|
|
id = binding->qualifiedTypeNameId;
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
QString name = id->name->asString();
|
|
|
|
if (!name.isEmpty() && name.at(0).isUpper()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline UiObjectInitializer *initializer(UiObjectMember *member) const
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
|
|
|
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member))
|
|
|
|
return def->initializer;
|
|
|
|
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(member))
|
|
|
|
return binding->initializer;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool hasVisualPresentation(Node *ast)
|
2010-07-27 11:57:06 +02:00
|
|
|
{
|
2010-08-26 10:50:00 +02:00
|
|
|
Bind *bind = m_lookupContext->document()->bind();
|
2010-07-27 11:57:06 +02:00
|
|
|
const Interpreter::ObjectValue *objValue = bind->findQmlObject(ast);
|
2010-11-12 14:53:00 +01:00
|
|
|
if (!objValue)
|
|
|
|
return false;
|
2010-07-27 11:57:06 +02:00
|
|
|
|
2010-11-12 14:53:00 +01:00
|
|
|
QStringList prototypes;
|
|
|
|
foreach (const Interpreter::ObjectValue *value,
|
|
|
|
Interpreter::PrototypeIterator(objValue, m_lookupContext->context()).all()) {
|
|
|
|
prototypes.append(value->className());
|
2010-07-27 11:57:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return prototypes.contains(QString("QGraphicsObject"));
|
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool isIdBinding(UiObjectMember *member) const
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
|
|
|
if (UiScriptBinding *script = cast<UiScriptBinding *>(member)) {
|
|
|
|
if (! script->qualifiedId)
|
|
|
|
return false;
|
|
|
|
else if (! script->qualifiedId->name)
|
|
|
|
return false;
|
|
|
|
else if (script->qualifiedId->next)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const QString propertyName = script->qualifiedId->name->asString();
|
|
|
|
|
|
|
|
if (propertyName == QLatin1String("id"))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool containsCursor(unsigned begin, unsigned end)
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return m_cursorPositionStart >= begin && m_cursorPositionEnd <= end;
|
2010-07-12 17:18:05 +02:00
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool intersectsCursor(unsigned begin, unsigned end)
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return (m_cursorPositionEnd >= begin && m_cursorPositionStart <= end);
|
2010-07-12 17:18:05 +02:00
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool isRangeSelected() const
|
2010-07-15 10:53:49 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return (m_cursorPositionStart != m_cursorPositionEnd);
|
2010-07-15 10:53:49 +02:00
|
|
|
}
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
void postVisit(Node *ast)
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
if (!isRangeSelected() && !m_selectedMembers.isEmpty())
|
2010-07-12 17:18:05 +02:00
|
|
|
return; // nothing to do, we already have the results.
|
2010-07-08 11:34:51 +02:00
|
|
|
|
|
|
|
if (UiObjectMember *member = ast->uiObjectMemberCast()) {
|
|
|
|
unsigned begin = member->firstSourceLocation().begin();
|
|
|
|
unsigned end = member->lastSourceLocation().end();
|
|
|
|
|
2010-07-15 10:53:49 +02:00
|
|
|
if ((isRangeSelected() && intersectsCursor(begin, end))
|
|
|
|
|| (!isRangeSelected() && containsCursor(begin, end)))
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
if (initializer(member) && isSelectable(member) && hasVisualPresentation(member)) {
|
|
|
|
m_selectedMembers << member;
|
|
|
|
// move start towards end; this facilitates multiselection so that root is usually ignored.
|
|
|
|
m_cursorPositionStart = qMin(end, m_cursorPositionEnd);
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-07-27 11:57:06 +02:00
|
|
|
void QmlJSTextEditor::setSelectedElements()
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2010-08-24 15:13:50 +02:00
|
|
|
if (!m_updateSelectedElements)
|
|
|
|
return;
|
|
|
|
|
2010-07-08 11:34:51 +02:00
|
|
|
QTextCursor tc = textCursor();
|
2010-07-12 17:18:05 +02:00
|
|
|
QString wordAtCursor;
|
|
|
|
QList<int> offsets;
|
|
|
|
|
|
|
|
unsigned startPos;
|
|
|
|
unsigned endPos;
|
|
|
|
|
|
|
|
if (tc.hasSelection()) {
|
|
|
|
startPos = tc.selectionStart();
|
|
|
|
endPos = tc.selectionEnd();
|
|
|
|
} else {
|
|
|
|
tc.movePosition(QTextCursor::StartOfWord);
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
startPos = textCursor().position();
|
|
|
|
endPos = textCursor().position();
|
|
|
|
}
|
2010-07-08 11:34:51 +02:00
|
|
|
|
2010-10-04 14:32:50 +02:00
|
|
|
if (m_semanticInfo.isValid()) {
|
2010-07-12 17:18:05 +02:00
|
|
|
SelectedElement selectedMembers;
|
2010-08-26 10:50:00 +02:00
|
|
|
QList<UiObjectMember *> members = selectedMembers(m_semanticInfo.lookupContext(),
|
2010-07-27 11:57:06 +02:00
|
|
|
startPos, endPos);
|
2010-07-12 17:18:05 +02:00
|
|
|
if (!members.isEmpty()) {
|
|
|
|
foreach(UiObjectMember *m, members) {
|
|
|
|
offsets << m->firstSourceLocation().begin();
|
|
|
|
}
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
}
|
2010-07-12 17:18:05 +02:00
|
|
|
wordAtCursor = tc.selectedText();
|
|
|
|
|
|
|
|
emit selectedElementsChanged(offsets, wordAtCursor);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::updateFileName()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::renameIdUnderCursor()
|
2009-05-05 15:33:39 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
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) {
|
2010-09-30 14:25:20 +02:00
|
|
|
renameId(id, newId);
|
2009-09-16 13:56:06 +02:00
|
|
|
}
|
2009-05-05 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
TextEditor::BaseTextEditor::setFontSettings(fs);
|
2010-02-15 12:27:25 +01:00
|
|
|
Highlighter *highlighter = qobject_cast<Highlighter*>(baseTextDocument()->syntaxHighlighter());
|
2009-09-16 13:56:06 +02:00
|
|
|
if (!highlighter)
|
|
|
|
return;
|
|
|
|
|
2010-10-27 17:38:22 +02:00
|
|
|
highlighter->setFormats(fs.toTextCharFormats(highlighterFormatCategories()));
|
2010-02-15 12:27:25 +01:00
|
|
|
highlighter->rehighlight();
|
|
|
|
|
2010-01-14 16:30:48 +01:00
|
|
|
m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
|
2010-02-15 12:27:25 +01:00
|
|
|
m_occurrencesUnusedFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_UNUSED));
|
|
|
|
m_occurrencesUnusedFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
|
|
m_occurrencesUnusedFormat.setUnderlineColor(m_occurrencesUnusedFormat.foreground().color());
|
|
|
|
m_occurrencesUnusedFormat.clearForeground();
|
|
|
|
m_occurrencesUnusedFormat.setToolTip(tr("Unused variable"));
|
|
|
|
m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
|
|
|
|
|
|
|
|
// only set the background, we do not want to modify foreground properties set by the syntax highlighter or the link
|
2010-01-14 16:30:48 +01:00
|
|
|
m_occurrencesFormat.clearForeground();
|
2010-02-15 12:27:25 +01:00
|
|
|
m_occurrenceRenameFormat.clearForeground();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-02-15 12:27:25 +01:00
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
QString QmlJSTextEditor::wordUnderCursor() const
|
2009-05-05 15:33:39 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
QTextCursor tc = textCursor();
|
2010-03-25 14:47:14 +01:00
|
|
|
const QChar ch = characterAt(tc.position() - 1);
|
|
|
|
// make sure that we're not at the start of the next word.
|
|
|
|
if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
|
|
|
|
tc.movePosition(QTextCursor::Left);
|
2009-09-16 13:56:06 +02:00
|
|
|
tc.movePosition(QTextCursor::StartOfWord);
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
2010-03-25 14:47:14 +01:00
|
|
|
const QString word = tc.selectedText();
|
2009-09-16 13:56:06 +02:00
|
|
|
return word;
|
2009-05-05 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
bool QmlJSTextEditor::isClosingBrace(const QList<Token> &tokens) const
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2009-11-16 12:38:47 +01:00
|
|
|
if (tokens.size() == 1) {
|
2010-01-19 12:54:17 +01:00
|
|
|
const Token firstToken = tokens.first();
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2010-01-19 12:54:17 +01:00
|
|
|
return firstToken.is(Token::RightBrace) || firstToken.is(Token::RightBracket);
|
2009-11-16 12:38:47 +01:00
|
|
|
}
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2009-11-16 12:38:47 +01:00
|
|
|
return false;
|
|
|
|
}
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
TextEditor::BaseTextEditorEditable *QmlJSTextEditor::createEditableInterface()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-01-15 17:20:03 +01:00
|
|
|
QmlJSEditorEditable *editable = new QmlJSEditorEditable(this);
|
2009-09-16 13:56:06 +02:00
|
|
|
createToolBar(editable);
|
|
|
|
return editable;
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::createToolBar(QmlJSEditorEditable *editable)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo = new QComboBox;
|
|
|
|
m_outlineCombo->setMinimumContentsLength(22);
|
|
|
|
m_outlineCombo->setModel(m_outlineModel);
|
2010-07-12 14:45:22 +02:00
|
|
|
|
|
|
|
QTreeView *treeView = new QTreeView;
|
|
|
|
treeView->header()->hide();
|
|
|
|
treeView->setItemsExpandable(false);
|
2010-07-12 15:49:03 +02:00
|
|
|
treeView->setRootIsDecorated(false);
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setView(treeView);
|
2010-07-12 14:45:22 +02:00
|
|
|
treeView->expandAll();
|
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
//m_outlineCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
// Make the combo box prefer to expand
|
2010-07-13 11:24:16 +02:00
|
|
|
QSizePolicy policy = m_outlineCombo->sizePolicy();
|
2009-09-16 13:56:06 +02:00
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setSizePolicy(policy);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-13 11:25:45 +02:00
|
|
|
connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
|
2010-07-13 11:24:16 +02:00
|
|
|
connect(this, SIGNAL(cursorPositionChanged()), m_updateOutlineIndexTimer, SLOT(start()));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QList<QAction*> actions = toolBar->actions();
|
2010-07-13 11:24:16 +02:00
|
|
|
toolBar->insertWidget(actions.first(), m_outlineCombo);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/)
|
2009-09-11 14:42:50 +02:00
|
|
|
{
|
2010-02-01 16:14:34 +01:00
|
|
|
const SemanticInfo semanticInfo = m_semanticInfo;
|
2010-09-08 10:11:44 +02:00
|
|
|
if (! semanticInfo.isValid())
|
|
|
|
return Link();
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
const unsigned cursorPosition = cursor.position();
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
AST::Node *node = semanticInfo.nodeUnderCursor(cursorPosition);
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2010-07-16 11:16:22 +02:00
|
|
|
if (AST::UiImport *importAst = cast<AST::UiImport *>(node)) {
|
|
|
|
// if it's a file import, link to the file
|
2010-09-16 15:29:37 +02:00
|
|
|
foreach (const Interpreter::ImportInfo &import, semanticInfo.document->bind()->imports()) {
|
|
|
|
if (import.ast() == importAst && import.type() == Interpreter::ImportInfo::FileImport) {
|
|
|
|
BaseTextEditor::Link link(import.name());
|
2010-07-16 11:16:22 +02:00
|
|
|
link.begin = importAst->firstSourceLocation().begin();
|
|
|
|
link.end = importAst->lastSourceLocation().end();
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Link();
|
|
|
|
}
|
|
|
|
|
2010-08-26 10:50:00 +02:00
|
|
|
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(semanticInfo.astPath(cursorPosition));
|
2010-11-16 14:39:54 +01:00
|
|
|
Evaluate evaluator(lookupContext->context());
|
|
|
|
const Interpreter::Value *value = evaluator.reference(node);
|
2010-02-08 12:50:10 +01:00
|
|
|
|
|
|
|
QString fileName;
|
|
|
|
int line = 0, column = 0;
|
|
|
|
|
|
|
|
if (! (value && value->getSourceLocation(&fileName, &line, &column)))
|
|
|
|
return Link();
|
|
|
|
|
2010-05-19 14:22:02 +02:00
|
|
|
BaseTextEditor::Link link;
|
2010-02-08 12:50:10 +01:00
|
|
|
link.fileName = fileName;
|
|
|
|
link.line = line;
|
|
|
|
link.column = column - 1; // adjust the column
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
if (AST::UiQualifiedId *q = AST::cast<AST::UiQualifiedId *>(node)) {
|
|
|
|
for (AST::UiQualifiedId *tail = q; tail; tail = tail->next) {
|
|
|
|
if (! tail->next && cursorPosition <= tail->identifierToken.end()) {
|
|
|
|
link.begin = tail->identifierToken.begin();
|
|
|
|
link.end = tail->identifierToken.end();
|
2010-02-01 16:14:34 +01:00
|
|
|
return link;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
} else if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node)) {
|
|
|
|
link.begin = id->firstSourceLocation().begin();
|
|
|
|
link.end = id->lastSourceLocation().end();
|
|
|
|
return link;
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
} else if (AST::FieldMemberExpression *mem = AST::cast<AST::FieldMemberExpression *>(node)) {
|
|
|
|
link.begin = mem->lastSourceLocation().begin();
|
|
|
|
link.end = mem->lastSourceLocation().end();
|
|
|
|
return link;
|
2010-02-01 16:14:34 +01:00
|
|
|
}
|
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
return Link();
|
2009-09-11 14:42:50 +02:00
|
|
|
}
|
|
|
|
|
2010-02-08 12:50:10 +01:00
|
|
|
void QmlJSTextEditor::followSymbolUnderCursor()
|
|
|
|
{
|
|
|
|
openLink(findLinkAt(textCursor()));
|
|
|
|
}
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
void QmlJSTextEditor::findUsages()
|
|
|
|
{
|
|
|
|
m_findReferences->findUsages(file()->fileName(), textCursor().position());
|
|
|
|
}
|
|
|
|
|
2010-08-04 13:50:15 +02:00
|
|
|
void QmlJSTextEditor::showContextPane()
|
|
|
|
{
|
2010-10-04 14:32:50 +02:00
|
|
|
if (m_contextPane && m_semanticInfo.isValid()) {
|
2010-08-04 13:50:15 +02:00
|
|
|
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
|
2010-10-21 15:41:58 +02:00
|
|
|
m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.lookupContext(), newNode, false, true);
|
2010-08-04 13:50:15 +02:00
|
|
|
m_oldCursorPosition = position();
|
2010-11-01 16:29:45 +01:00
|
|
|
QList<TextEditor::RefactorMarker> markers;
|
2010-08-05 15:35:52 +02:00
|
|
|
setRefactorMarkers(markers);
|
2010-08-04 13:50:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-16 12:57:07 +02:00
|
|
|
void QmlJSTextEditor::performQuickFix(int index)
|
|
|
|
{
|
|
|
|
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
|
|
|
|
op->perform();
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::contextMenuEvent(QContextMenuEvent *e)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2009-11-09 17:35:20 +01:00
|
|
|
QMenu *menu = new QMenu();
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2010-09-16 12:57:07 +02:00
|
|
|
QMenu *refactoringMenu = new QMenu(tr("Refactoring"), menu);
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2010-09-16 12:57:07 +02:00
|
|
|
// Conditionally add the rename-id action:
|
2009-09-16 13:56:06 +02:00
|
|
|
const QString id = wordUnderCursor();
|
2010-01-26 14:14:01 +01:00
|
|
|
const QList<AST::SourceLocation> &locations = m_semanticInfo.idLocations.value(id);
|
2009-09-16 13:56:06 +02:00
|
|
|
if (! locations.isEmpty()) {
|
2010-09-16 12:57:07 +02:00
|
|
|
QAction *a = refactoringMenu->addAction(tr("Rename id '%1'...").arg(id));
|
2009-09-16 13:56:06 +02:00
|
|
|
connect(a, SIGNAL(triggered()), this, SLOT(renameIdUnderCursor()));
|
|
|
|
}
|
|
|
|
|
2010-09-16 12:57:07 +02:00
|
|
|
// Add other refactoring actions:
|
|
|
|
QmlJSQuickFixCollector *quickFixCollector = QmlJSEditorPlugin::instance()->quickFixCollector();
|
|
|
|
QSignalMapper mapper;
|
|
|
|
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
|
|
|
|
|
|
|
|
if (! isOutdated()) {
|
|
|
|
if (quickFixCollector->startCompletion(editableInterface()) != -1) {
|
|
|
|
m_quickFixes = quickFixCollector->quickFixes();
|
|
|
|
|
|
|
|
for (int index = 0; index < m_quickFixes.size(); ++index) {
|
|
|
|
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
|
|
|
|
QAction *action = refactoringMenu->addAction(op->description());
|
|
|
|
mapper.setMapping(action, index);
|
|
|
|
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
refactoringMenu->setEnabled(!refactoringMenu->isEmpty());
|
|
|
|
|
|
|
|
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);
|
|
|
|
if (action->objectName() == QmlJSEditor::Constants::M_REFACTORING_MENU_INSERTION_POINT)
|
|
|
|
menu->addMenu(refactoringMenu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-09 17:35:20 +01:00
|
|
|
appendStandardContextMenuActions(menu);
|
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
menu->exec(e->globalPos());
|
|
|
|
menu->deleteLater();
|
2010-09-16 12:57:07 +02:00
|
|
|
quickFixCollector->cleanup();
|
|
|
|
m_quickFixes.clear();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
2010-07-07 13:09:30 +02:00
|
|
|
bool QmlJSTextEditor::event(QEvent *e)
|
|
|
|
{
|
|
|
|
switch (e->type()) {
|
|
|
|
case QEvent::ShortcutOverride:
|
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_contextPane) {
|
2010-07-20 15:01:06 +02:00
|
|
|
if (hideContextPane()) {
|
2010-07-15 16:40:19 +02:00
|
|
|
e->accept();
|
|
|
|
return true;
|
|
|
|
}
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BaseTextEditor::event(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QmlJSTextEditor::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
2010-07-20 15:01:06 +02:00
|
|
|
bool visible = false;
|
|
|
|
if (m_contextPane && m_contextPane->widget()->isVisible())
|
|
|
|
visible = true;
|
|
|
|
|
2010-07-07 13:09:30 +02:00
|
|
|
BaseTextEditor::wheelEvent(event);
|
2010-07-20 15:01:06 +02:00
|
|
|
|
2010-10-04 14:32:50 +02:00
|
|
|
if (visible) {
|
|
|
|
LookupContext::Ptr lookupContext;
|
|
|
|
if (m_semanticInfo.isValid())
|
|
|
|
lookupContext = m_semanticInfo.lookupContext();
|
2010-10-21 15:41:58 +02:00
|
|
|
m_contextPane->apply(editableInterface(), semanticInfo().document, QmlJS::LookupContext::Ptr(), m_semanticInfo.declaringMemberNoProperties(m_oldCursorPosition), false, true);
|
2010-10-04 14:32:50 +02:00
|
|
|
}
|
2010-07-20 15:01:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmlJSTextEditor::resizeEvent(QResizeEvent *event)
|
|
|
|
{
|
|
|
|
BaseTextEditor::resizeEvent(event);
|
|
|
|
hideContextPane();
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
|
2010-07-20 15:01:06 +02:00
|
|
|
void QmlJSTextEditor::scrollContentsBy(int dx, int dy)
|
|
|
|
{
|
|
|
|
BaseTextEditor::scrollContentsBy(dx, dy);
|
|
|
|
hideContextPane();
|
|
|
|
}
|
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
void QmlJSTextEditor::unCommentSelection()
|
2009-05-14 16:37:17 +02:00
|
|
|
{
|
2009-10-05 11:06:05 +02:00
|
|
|
Utils::unCommentSelection(this);
|
2009-05-14 16:37:17 +02:00
|
|
|
}
|
|
|
|
|
2010-04-06 11:57:08 +02:00
|
|
|
void QmlJSTextEditor::forceSemanticRehighlight()
|
|
|
|
{
|
|
|
|
m_semanticHighlighter->rehighlight(currentSource(/* force = */ true));
|
|
|
|
}
|
|
|
|
|
2010-02-16 10:36:09 +01:00
|
|
|
void QmlJSTextEditor::semanticRehighlight()
|
|
|
|
{
|
|
|
|
m_semanticHighlighter->rehighlight(currentSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
|
|
|
{
|
|
|
|
if (semanticInfo.revision() != document()->revision()) {
|
|
|
|
// got outdated semantic info
|
|
|
|
semanticRehighlight();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_semanticInfo = semanticInfo;
|
|
|
|
Document::Ptr doc = semanticInfo.document;
|
|
|
|
|
|
|
|
// create the ranges
|
|
|
|
CreateRanges createRanges;
|
|
|
|
m_semanticInfo.ranges = createRanges(document(), doc);
|
|
|
|
|
|
|
|
// Refresh the ids
|
|
|
|
FindIdDeclarations updateIds;
|
|
|
|
m_semanticInfo.idLocations = updateIds(doc);
|
|
|
|
|
2010-08-05 15:35:52 +02:00
|
|
|
FindDeclarations findDeclarations;
|
|
|
|
m_semanticInfo.declarations = findDeclarations(doc->ast());
|
2010-02-16 10:36:09 +01:00
|
|
|
|
2010-08-05 15:35:52 +02:00
|
|
|
if (m_contextPane) {
|
|
|
|
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
|
|
|
|
if (newNode) {
|
2010-10-21 15:41:58 +02:00
|
|
|
m_contextPane->apply(editableInterface(), semanticInfo.document, LookupContext::Ptr(), newNode, true);
|
2010-09-08 16:34:49 +02:00
|
|
|
m_cursorPositionTimer->start(); //update text marker
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
2010-07-19 11:39:41 +02:00
|
|
|
// update outline
|
|
|
|
m_updateOutlineTimer->start();
|
|
|
|
|
2010-02-16 10:36:09 +01:00
|
|
|
// update warning/error extra selections
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
|
|
|
|
appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document());
|
|
|
|
setExtraSelections(CodeWarningsSelection, selections);
|
|
|
|
}
|
|
|
|
|
2010-11-01 16:29:45 +01:00
|
|
|
void QmlJSTextEditor::onRefactorMarkerClicked(const TextEditor::RefactorMarker &)
|
2010-08-05 10:44:50 +02:00
|
|
|
{
|
|
|
|
showContextPane();
|
|
|
|
}
|
|
|
|
|
2010-07-07 13:09:30 +02:00
|
|
|
void QmlJSTextEditor::onCursorPositionChanged()
|
|
|
|
{
|
2010-08-27 10:32:04 +02:00
|
|
|
m_cursorPositionTimer->start();
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
QModelIndex QmlJSTextEditor::indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex) const
|
|
|
|
{
|
|
|
|
QModelIndex lastIndex = rootIndex;
|
|
|
|
|
|
|
|
|
|
|
|
const int rowCount = m_outlineModel->rowCount(rootIndex);
|
|
|
|
for (int i = 0; i < rowCount; ++i) {
|
|
|
|
QModelIndex childIndex = m_outlineModel->index(i, 0, rootIndex);
|
2010-08-25 13:21:38 +02:00
|
|
|
AST::SourceLocation location = m_outlineModel->sourceLocation(childIndex);
|
2010-07-12 14:45:22 +02:00
|
|
|
|
|
|
|
if ((cursorPosition >= location.offset)
|
|
|
|
&& (cursorPosition <= location.offset + location.length)) {
|
|
|
|
lastIndex = childIndex;
|
|
|
|
break;
|
|
|
|
}
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
2010-07-12 14:45:22 +02:00
|
|
|
|
|
|
|
if (lastIndex != rootIndex) {
|
|
|
|
// recurse
|
|
|
|
lastIndex = indexForPosition(cursorPosition, lastIndex);
|
|
|
|
}
|
|
|
|
return lastIndex;
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
2010-07-20 15:01:06 +02:00
|
|
|
bool QmlJSTextEditor::hideContextPane()
|
|
|
|
{
|
|
|
|
bool b = (m_contextPane) && m_contextPane->widget()->isVisible();
|
2010-10-04 14:32:50 +02:00
|
|
|
if (b) {
|
2010-10-21 15:41:58 +02:00
|
|
|
m_contextPane->apply(editableInterface(), semanticInfo().document, LookupContext::Ptr(), 0, false);
|
2010-10-04 14:32:50 +02:00
|
|
|
}
|
2010-07-20 15:01:06 +02:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2010-10-27 17:38:22 +02:00
|
|
|
QVector<QString> QmlJSTextEditor::highlighterFormatCategories()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
NumberFormat,
|
|
|
|
StringFormat,
|
|
|
|
TypeFormat,
|
|
|
|
KeywordFormat,
|
|
|
|
LabelFormat,
|
|
|
|
CommentFormat,
|
|
|
|
VisualWhitespace,
|
|
|
|
*/
|
|
|
|
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_FIELD)
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_COMMENT)
|
|
|
|
<< QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE);
|
|
|
|
}
|
|
|
|
return categories;
|
|
|
|
}
|
|
|
|
|
2010-11-02 11:10:27 +01:00
|
|
|
SemanticHighlighterSource QmlJSTextEditor::currentSource(bool force)
|
2010-02-16 10:36:09 +01:00
|
|
|
{
|
|
|
|
int line = 0, column = 0;
|
|
|
|
convertPosition(position(), &line, &column);
|
|
|
|
|
|
|
|
const Snapshot snapshot = m_modelManager->snapshot();
|
|
|
|
const QString fileName = file()->fileName();
|
|
|
|
|
|
|
|
QString code;
|
|
|
|
if (force || m_semanticInfo.revision() != document()->revision())
|
|
|
|
code = toPlainText(); // get the source code only when needed.
|
|
|
|
|
|
|
|
const unsigned revision = document()->revision();
|
2010-11-02 11:10:27 +01:00
|
|
|
SemanticHighlighterSource source(snapshot, fileName, code,
|
2010-02-16 10:36:09 +01:00
|
|
|
line, column, revision);
|
|
|
|
source.force = force;
|
|
|
|
return source;
|
|
|
|
}
|