diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 7a580d18ce62d9ae71333a75405014bc12f9b451..9f95eb922a0aa8d2373972d2b79db66e8ed16d61 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -139,7 +139,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia } // normal component instance - ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp); + ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _doc, &_interp); QmlPrototypeReference *prototypeReference = new QmlPrototypeReference(qualifiedTypeNameId, _doc, &_interp); objectValue->setPrototype(prototypeReference); diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index f25a7df52f0e68469b6f371f2420979237cd6555..0cb1e77b9a3d96e7e3174e74e2feff67bd7f8646 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -51,10 +51,18 @@ Check::~Check() const Interpreter::Value *Check::operator()(AST::Node *ast) { - return check(ast); + const Value *result = reference(ast); + + if (const Reference *ref = value_cast<const Reference *>(result)) + result = ref->value(_context); + + if (! result) + result = _engine->undefinedValue(); + + return result; } -const Interpreter::Value *Check::check(AST::Node *ast) +const Interpreter::Value *Check::reference(AST::Node *ast) { // save the result const Value *previousResult = switchResult(0); @@ -63,15 +71,7 @@ const Interpreter::Value *Check::check(AST::Node *ast) accept(ast); // restore the previous result - const Value *result = switchResult(previousResult); - - if (const Reference *ref = value_cast<const Reference *>(result)) - result = ref->value(_context); - - if (! result) - result = _engine->undefinedValue(); - - return result; + return switchResult(previousResult); } Interpreter::Engine *Check::switchEngine(Interpreter::Engine *engine) @@ -309,7 +309,7 @@ bool Check::visit(AST::FieldMemberExpression *ast) if (! ast->name) return false; - if (const Interpreter::Value *base = _engine->convertToObject(check(ast->base))) { + if (const Interpreter::Value *base = _engine->convertToObject(reference(ast->base))) { if (const Interpreter::ObjectValue *obj = base->asObjectValue()) { _result = obj->property(ast->name->asString(), _context); } @@ -320,7 +320,7 @@ bool Check::visit(AST::FieldMemberExpression *ast) bool Check::visit(AST::NewMemberExpression *ast) { - if (const FunctionValue *ctor = value_cast<const FunctionValue *>(check(ast->base))) { + if (const FunctionValue *ctor = value_cast<const FunctionValue *>(reference(ast->base))) { _result = ctor->construct(); } return false; @@ -328,7 +328,7 @@ bool Check::visit(AST::NewMemberExpression *ast) bool Check::visit(AST::NewExpression *ast) { - if (const FunctionValue *ctor = value_cast<const FunctionValue *>(check(ast->expression))) { + if (const FunctionValue *ctor = value_cast<const FunctionValue *>(reference(ast->expression))) { _result = ctor->construct(); } return false; @@ -336,7 +336,7 @@ bool Check::visit(AST::NewExpression *ast) bool Check::visit(AST::CallExpression *ast) { - if (const Interpreter::Value *base = check(ast->base)) { + if (const Interpreter::Value *base = reference(ast->base)) { if (const Interpreter::FunctionValue *obj = base->asFunctionValue()) { _result = obj->returnValue(); } diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 14a56f8507d4a0c151abe3fe707f8df76eb9f59f..d0555f6754375f2ac5973d54e51cb9628fef1a49 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -52,10 +52,10 @@ public: virtual ~Check(); const Interpreter::Value *operator()(AST::Node *ast); + const Interpreter::Value *reference(AST::Node *ast); protected: void accept(AST::Node *node); - const Interpreter::Value *check(AST::Node *ast); Interpreter::Engine *switchEngine(Interpreter::Engine *engine); const Interpreter::Value *switchResult(const Interpreter::Value *result); diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index b48e28e0bc4e57829127fa0c3dec236326c6a8b2..9ac782bccc154e48fee21670c639f75e5280308c 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -585,6 +585,11 @@ Value::~Value() { } +bool Value::getSourceLocation(QString *, int *, int *) const +{ + return false; +} + const NullValue *Value::asNullValue() const { return 0; @@ -1909,30 +1914,42 @@ QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix, -ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Engine *engine) - : ObjectValue(engine), _typeName(typeName), _initializer(initializer) -{ -} - -ASTObjectValue::~ASTObjectValue() -{ -} - -void ASTObjectValue::processMembers(MemberProcessor *processor) const +ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, + UiObjectInitializer *initializer, + const Document *doc, + Engine *engine) + : ObjectValue(engine), _typeName(typeName), _initializer(initializer), _doc(doc) { if (_initializer) { for (UiObjectMemberList *it = _initializer->members; it; it = it->next) { UiObjectMember *member = it->member; if (UiPublicMember *def = cast<UiPublicMember *>(member)) { if (def->name && def->memberType) { - const QString propName = def->name->asString(); - const QString propType = def->memberType->asString(); - - processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType)); + ASTPropertyReference *ref = new ASTPropertyReference(def, _doc, engine); + _properties.append(ref); } } } } +} + +ASTObjectValue::~ASTObjectValue() +{ +} + +bool ASTObjectValue::getSourceLocation(QString *fileName, int *line, int *column) const +{ + *fileName = _doc->fileName(); + *line = _typeName->identifierToken.startLine; + *column = _typeName->identifierToken.startColumn; + return true; +} + +void ASTObjectValue::processMembers(MemberProcessor *processor) const +{ + foreach (ASTPropertyReference *ref, _properties) + processor->processProperty(ref->ast()->name->asString(), ref); + ObjectValue::processMembers(processor); } @@ -2021,3 +2038,32 @@ const Value *QmlPrototypeReference::value(Context *context) const return context->lookupType(_doc, _qmlTypeName); } +ASTPropertyReference::ASTPropertyReference(AST::UiPublicMember *ast, const Document *doc, Engine *engine) + : Reference(engine), _ast(ast), _doc(doc) +{ +} + +ASTPropertyReference::~ASTPropertyReference() +{ +} + +bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int *column) const +{ + *fileName = _doc->fileName(); + *line = _ast->identifierToken.startLine; + *column = _ast->identifierToken.startColumn; + return true; +} + +const Value *ASTPropertyReference::value(Context *context) const +{ + if (_ast->expression) { + Check check(context); + return check(_ast->expression); + } + + if (_ast->memberType) + return engine()->defaultValueForBuiltinType(_ast->memberType->asString()); + + return engine()->undefinedValue(); +} diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 88a958a64fa88679f30139f97c2d59df9af06d71..3a03be7a642030c6df4e1abbfaee66e1b8405b25 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -104,6 +104,8 @@ public: virtual const Reference *asReference() const; virtual void accept(ValueVisitor *) const = 0; + + virtual bool getSourceLocation(QString *fileName, int *line, int *column) const; }; template <typename _RetTy> _RetTy value_cast(const Value *v); @@ -225,7 +227,7 @@ public: Context(Engine *engine); ~Context(); - void build(AST::Node *node, Document::Ptr doc, const Snapshot &snapshot); + void build(AST::Node *node, const Document::Ptr doc, const Snapshot &snapshot); Engine *engine() const; ScopeChain scopeChain() const; @@ -642,18 +644,6 @@ private: const Document *_doc; }; -class QMLJS_EXPORT ASTObjectValue: public ObjectValue -{ - AST::UiQualifiedId *_typeName; - AST::UiObjectInitializer *_initializer; - -public: - ASTObjectValue(AST::UiQualifiedId *typeName, AST::UiObjectInitializer *initializer, Engine *engine); - virtual ~ASTObjectValue(); - - virtual void processMembers(MemberProcessor *processor) const; -}; - class QMLJS_EXPORT ASTVariableReference: public Reference { AST::VariableDeclaration *_ast; @@ -683,7 +673,38 @@ public: virtual bool isVariadic() const; }; +class QMLJS_EXPORT ASTPropertyReference: public Reference +{ + AST::UiPublicMember *_ast; + const Document *_doc; + +public: + ASTPropertyReference(AST::UiPublicMember *ast, const Document *doc, Engine *engine); + virtual ~ASTPropertyReference(); + AST::UiPublicMember *ast() const { return _ast; } + + virtual bool getSourceLocation(QString *fileName, int *line, int *column) const; + virtual const Value *value(Context *context) const; +}; + +class QMLJS_EXPORT ASTObjectValue: public ObjectValue +{ + AST::UiQualifiedId *_typeName; + AST::UiObjectInitializer *_initializer; + const Document *_doc; + QList<ASTPropertyReference *> _properties; + +public: + ASTObjectValue(AST::UiQualifiedId *typeName, + AST::UiObjectInitializer *initializer, + const Document *doc, + Engine *engine); + virtual ~ASTObjectValue(); + + bool getSourceLocation(QString *fileName, int *line, int *column) const; + virtual void processMembers(MemberProcessor *processor) const; +}; } } // end of namespace QmlJS::Interpreter diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 19c3d441dd1ff7f670568d2d47535290c253eb31..786d6e76418fb78ac1bdaca8b7230d2f384fb56e 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -910,10 +910,26 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & AST::Node *node = semanticInfo.nodeUnderCursor(cursorPosition); + Interpreter::Engine interp; + Interpreter::Context context(&interp); + context.build(semanticInfo.declaringMember(cursorPosition), semanticInfo.document, semanticInfo.snapshot); + + Check check(&context); + const Interpreter::Value *value = check.reference(node); + QString fileName; + int line = 0, column = 0; + + if (! (value && value->getSourceLocation(&fileName, &line, &column))) + return Link(); + + Link link; + link.fileName = fileName; + link.line = line; + link.column = column - 1; // adjust the column + 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 link; link.begin = tail->identifierToken.begin(); link.end = tail->identifierToken.end(); return link; @@ -921,13 +937,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & } } else if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node)) { - Link link; link.begin = id->firstSourceLocation().begin(); link.end = id->lastSourceLocation().end(); return link; } else if (AST::FieldMemberExpression *mem = AST::cast<AST::FieldMemberExpression *>(node)) { - Link link; link.begin = mem->lastSourceLocation().begin(); link.end = mem->lastSourceLocation().end(); return link; @@ -936,6 +950,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & return Link(); } +void QmlJSTextEditor::followSymbolUnderCursor() +{ + openLink(findLinkAt(textCursor())); +} + void QmlJSTextEditor::contextMenuEvent(QContextMenuEvent *e) { QMenu *menu = new QMenu(); diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index c332cc66d698165b7256f1b7552588737fe0dd0f..da3458269cc968a8bf816351195329450167ee42 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -135,6 +135,7 @@ public: int documentRevision() const; public slots: + void followSymbolUnderCursor(); virtual void setFontSettings(const TextEditor::FontSettings &); private slots: diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h index 7996a6c9f96e4a546ffdc59dc7b4cbf012b9adab..8a9a99d7eb03687a090761e9d260e21b83dc6d1c 100644 --- a/src/plugins/qmljseditor/qmljseditorconstants.h +++ b/src/plugins/qmljseditor/qmljseditorconstants.h @@ -41,6 +41,8 @@ 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 FOLLOW_SYMBOL_UNDER_CURSOR = "QmlJSEditor.FollowSymbolUnderCursor"; + const char * const QML_MIMETYPE = "application/x-qml"; const char * const JS_MIMETYPE = "application/javascript"; diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index 894c5745073e27d35b344859c8a822f2aeecb2a3..7df0321ea7e57f2e718637d0e938f0e892834991 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -43,6 +43,7 @@ #include <coreplugin/mimedatabase.h> #include <coreplugin/uniqueidmanager.h> #include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/editormanager/editormanager.h> #include <extensionsystem/pluginmanager.h> #include <texteditor/fontsettings.h> #include <texteditor/storagesettings.h> @@ -114,9 +115,19 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e 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); + Core::ActionContainer *contextMenu = am->createMenu(QmlJSEditor::Constants::M_CONTEXT); + + Core::Command *cmd = 0; + + QAction *followSymbolUnderCursorAction = new QAction(tr("Follow Symbol Under Cursor"), this); + cmd = am->registerAction(followSymbolUnderCursorAction, Constants::FOLLOW_SYMBOL_UNDER_CURSOR, context); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2)); + connect(followSymbolUnderCursorAction, SIGNAL(triggered()), this, SLOT(followSymbolUnderCursor())); contextMenu->addAction(cmd); + + cmd = am->command(TextEditor::Constants::AUTO_INDENT_SELECTION); + contextMenu->addAction(cmd); + cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION); contextMenu->addAction(cmd); @@ -177,4 +188,12 @@ void QmlJSEditorPlugin::initializeEditor(QmlJSEditor::Internal::QmlJSTextEditor TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(TextEditor::ITextEditable*, bool))); } +void QmlJSEditorPlugin::followSymbolUnderCursor() +{ + Core::EditorManager *em = Core::EditorManager::instance(); + + if (QmlJSTextEditor *editor = qobject_cast<QmlJSTextEditor*>(em->currentEditor()->widget())) + editor->followSymbolUnderCursor(); +} + Q_EXPORT_PLUGIN(QmlJSEditorPlugin) diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h index 660b118903be90d7e54016373de1e3276e0f48a8..700842898b60fd88a169fdcb0cf6b2a0af27ae63 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.h +++ b/src/plugins/qmljseditor/qmljseditorplugin.h @@ -64,6 +64,9 @@ public: void initializeEditor(QmlJSTextEditor *editor); +public Q_SLOTS: + void followSymbolUnderCursor(); + private: static QmlJSEditorPlugin *m_instance;