Skip to content
Snippets Groups Projects
Commit 967ed09c authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Initial support of `Follow symbol under cursor' for QML/JS.

parent b02d2f9f
No related branches found
No related tags found
No related merge requests found
...@@ -139,7 +139,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia ...@@ -139,7 +139,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
} }
// normal component instance // normal component instance
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp); ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _doc, &_interp);
QmlPrototypeReference *prototypeReference = QmlPrototypeReference *prototypeReference =
new QmlPrototypeReference(qualifiedTypeNameId, _doc, &_interp); new QmlPrototypeReference(qualifiedTypeNameId, _doc, &_interp);
objectValue->setPrototype(prototypeReference); objectValue->setPrototype(prototypeReference);
......
...@@ -51,10 +51,18 @@ Check::~Check() ...@@ -51,10 +51,18 @@ Check::~Check()
const Interpreter::Value *Check::operator()(AST::Node *ast) 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 // save the result
const Value *previousResult = switchResult(0); const Value *previousResult = switchResult(0);
...@@ -63,15 +71,7 @@ const Interpreter::Value *Check::check(AST::Node *ast) ...@@ -63,15 +71,7 @@ const Interpreter::Value *Check::check(AST::Node *ast)
accept(ast); accept(ast);
// restore the previous result // restore the previous result
const Value *result = switchResult(previousResult); return switchResult(previousResult);
if (const Reference *ref = value_cast<const Reference *>(result))
result = ref->value(_context);
if (! result)
result = _engine->undefinedValue();
return result;
} }
Interpreter::Engine *Check::switchEngine(Interpreter::Engine *engine) Interpreter::Engine *Check::switchEngine(Interpreter::Engine *engine)
...@@ -309,7 +309,7 @@ bool Check::visit(AST::FieldMemberExpression *ast) ...@@ -309,7 +309,7 @@ bool Check::visit(AST::FieldMemberExpression *ast)
if (! ast->name) if (! ast->name)
return false; 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()) { if (const Interpreter::ObjectValue *obj = base->asObjectValue()) {
_result = obj->property(ast->name->asString(), _context); _result = obj->property(ast->name->asString(), _context);
} }
...@@ -320,7 +320,7 @@ bool Check::visit(AST::FieldMemberExpression *ast) ...@@ -320,7 +320,7 @@ bool Check::visit(AST::FieldMemberExpression *ast)
bool Check::visit(AST::NewMemberExpression *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(); _result = ctor->construct();
} }
return false; return false;
...@@ -328,7 +328,7 @@ bool Check::visit(AST::NewMemberExpression *ast) ...@@ -328,7 +328,7 @@ bool Check::visit(AST::NewMemberExpression *ast)
bool Check::visit(AST::NewExpression *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(); _result = ctor->construct();
} }
return false; return false;
...@@ -336,7 +336,7 @@ bool Check::visit(AST::NewExpression *ast) ...@@ -336,7 +336,7 @@ bool Check::visit(AST::NewExpression *ast)
bool Check::visit(AST::CallExpression *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()) { if (const Interpreter::FunctionValue *obj = base->asFunctionValue()) {
_result = obj->returnValue(); _result = obj->returnValue();
} }
......
...@@ -52,10 +52,10 @@ public: ...@@ -52,10 +52,10 @@ public:
virtual ~Check(); virtual ~Check();
const Interpreter::Value *operator()(AST::Node *ast); const Interpreter::Value *operator()(AST::Node *ast);
const Interpreter::Value *reference(AST::Node *ast);
protected: protected:
void accept(AST::Node *node); void accept(AST::Node *node);
const Interpreter::Value *check(AST::Node *ast);
Interpreter::Engine *switchEngine(Interpreter::Engine *engine); Interpreter::Engine *switchEngine(Interpreter::Engine *engine);
const Interpreter::Value *switchResult(const Interpreter::Value *result); const Interpreter::Value *switchResult(const Interpreter::Value *result);
......
...@@ -585,6 +585,11 @@ Value::~Value() ...@@ -585,6 +585,11 @@ Value::~Value()
{ {
} }
bool Value::getSourceLocation(QString *, int *, int *) const
{
return false;
}
const NullValue *Value::asNullValue() const const NullValue *Value::asNullValue() const
{ {
return 0; return 0;
...@@ -1909,30 +1914,42 @@ QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix, ...@@ -1909,30 +1914,42 @@ QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix,
ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Engine *engine) ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName,
: ObjectValue(engine), _typeName(typeName), _initializer(initializer) UiObjectInitializer *initializer,
{ const Document *doc,
} Engine *engine)
: ObjectValue(engine), _typeName(typeName), _initializer(initializer), _doc(doc)
ASTObjectValue::~ASTObjectValue()
{
}
void ASTObjectValue::processMembers(MemberProcessor *processor) const
{ {
if (_initializer) { if (_initializer) {
for (UiObjectMemberList *it = _initializer->members; it; it = it->next) { for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
UiObjectMember *member = it->member; UiObjectMember *member = it->member;
if (UiPublicMember *def = cast<UiPublicMember *>(member)) { if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
if (def->name && def->memberType) { if (def->name && def->memberType) {
const QString propName = def->name->asString(); ASTPropertyReference *ref = new ASTPropertyReference(def, _doc, engine);
const QString propType = def->memberType->asString(); _properties.append(ref);
processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType));
} }
} }
} }
} }
}
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); ObjectValue::processMembers(processor);
} }
...@@ -2021,3 +2038,32 @@ const Value *QmlPrototypeReference::value(Context *context) const ...@@ -2021,3 +2038,32 @@ const Value *QmlPrototypeReference::value(Context *context) const
return context->lookupType(_doc, _qmlTypeName); 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();
}
...@@ -104,6 +104,8 @@ public: ...@@ -104,6 +104,8 @@ public:
virtual const Reference *asReference() const; virtual const Reference *asReference() const;
virtual void accept(ValueVisitor *) const = 0; 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); template <typename _RetTy> _RetTy value_cast(const Value *v);
...@@ -225,7 +227,7 @@ public: ...@@ -225,7 +227,7 @@ public:
Context(Engine *engine); Context(Engine *engine);
~Context(); ~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; Engine *engine() const;
ScopeChain scopeChain() const; ScopeChain scopeChain() const;
...@@ -642,18 +644,6 @@ private: ...@@ -642,18 +644,6 @@ private:
const Document *_doc; 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 class QMLJS_EXPORT ASTVariableReference: public Reference
{ {
AST::VariableDeclaration *_ast; AST::VariableDeclaration *_ast;
...@@ -683,7 +673,38 @@ public: ...@@ -683,7 +673,38 @@ public:
virtual bool isVariadic() const; 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 } } // end of namespace QmlJS::Interpreter
......
...@@ -910,10 +910,26 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & ...@@ -910,10 +910,26 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
AST::Node *node = semanticInfo.nodeUnderCursor(cursorPosition); 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)) { if (AST::UiQualifiedId *q = AST::cast<AST::UiQualifiedId *>(node)) {
for (AST::UiQualifiedId *tail = q; tail; tail = tail->next) { for (AST::UiQualifiedId *tail = q; tail; tail = tail->next) {
if (! tail->next && cursorPosition <= tail->identifierToken.end()) { if (! tail->next && cursorPosition <= tail->identifierToken.end()) {
Link link;
link.begin = tail->identifierToken.begin(); link.begin = tail->identifierToken.begin();
link.end = tail->identifierToken.end(); link.end = tail->identifierToken.end();
return link; return link;
...@@ -921,13 +937,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & ...@@ -921,13 +937,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
} }
} else if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node)) { } else if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node)) {
Link link;
link.begin = id->firstSourceLocation().begin(); link.begin = id->firstSourceLocation().begin();
link.end = id->lastSourceLocation().end(); link.end = id->lastSourceLocation().end();
return link; return link;
} else if (AST::FieldMemberExpression *mem = AST::cast<AST::FieldMemberExpression *>(node)) { } else if (AST::FieldMemberExpression *mem = AST::cast<AST::FieldMemberExpression *>(node)) {
Link link;
link.begin = mem->lastSourceLocation().begin(); link.begin = mem->lastSourceLocation().begin();
link.end = mem->lastSourceLocation().end(); link.end = mem->lastSourceLocation().end();
return link; return link;
...@@ -936,6 +950,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & ...@@ -936,6 +950,11 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
return Link(); return Link();
} }
void QmlJSTextEditor::followSymbolUnderCursor()
{
openLink(findLinkAt(textCursor()));
}
void QmlJSTextEditor::contextMenuEvent(QContextMenuEvent *e) void QmlJSTextEditor::contextMenuEvent(QContextMenuEvent *e)
{ {
QMenu *menu = new QMenu(); QMenu *menu = new QMenu();
......
...@@ -135,6 +135,7 @@ public: ...@@ -135,6 +135,7 @@ public:
int documentRevision() const; int documentRevision() const;
public slots: public slots:
void followSymbolUnderCursor();
virtual void setFontSettings(const TextEditor::FontSettings &); virtual void setFontSettings(const TextEditor::FontSettings &);
private slots: private slots:
......
...@@ -41,6 +41,8 @@ const char * const C_QMLJSEDITOR_ID = "QMLProjectManager.QMLJSEditor"; ...@@ -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 C_QMLJSEDITOR_DISPLAY_NAME = QT_TRANSLATE_NOOP("OpenWith::Editors", "QMLJS Editor");
const char * const TASK_INDEX = "QmlJSEditor.TaskIndex"; 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 QML_MIMETYPE = "application/x-qml";
const char * const JS_MIMETYPE = "application/javascript"; const char * const JS_MIMETYPE = "application/javascript";
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <coreplugin/mimedatabase.h> #include <coreplugin/mimedatabase.h>
#include <coreplugin/uniqueidmanager.h> #include <coreplugin/uniqueidmanager.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/storagesettings.h> #include <texteditor/storagesettings.h>
...@@ -114,9 +115,19 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e ...@@ -114,9 +115,19 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
m_actionHandler->initializeActions(); m_actionHandler->initializeActions();
Core::ActionManager *am = core->actionManager(); Core::ActionManager *am = core->actionManager();
Core::ActionContainer *contextMenu= am->createMenu(QmlJSEditor::Constants::M_CONTEXT); Core::ActionContainer *contextMenu = am->createMenu(QmlJSEditor::Constants::M_CONTEXT);
Core::Command *cmd = am->command(TextEditor::Constants::AUTO_INDENT_SELECTION);
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); contextMenu->addAction(cmd);
cmd = am->command(TextEditor::Constants::AUTO_INDENT_SELECTION);
contextMenu->addAction(cmd);
cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION); cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
contextMenu->addAction(cmd); contextMenu->addAction(cmd);
...@@ -177,4 +188,12 @@ void QmlJSEditorPlugin::initializeEditor(QmlJSEditor::Internal::QmlJSTextEditor ...@@ -177,4 +188,12 @@ void QmlJSEditorPlugin::initializeEditor(QmlJSEditor::Internal::QmlJSTextEditor
TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(TextEditor::ITextEditable*, bool))); 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) Q_EXPORT_PLUGIN(QmlJSEditorPlugin)
...@@ -64,6 +64,9 @@ public: ...@@ -64,6 +64,9 @@ public:
void initializeEditor(QmlJSTextEditor *editor); void initializeEditor(QmlJSTextEditor *editor);
public Q_SLOTS:
void followSymbolUnderCursor();
private: private:
static QmlJSEditorPlugin *m_instance; static QmlJSEditorPlugin *m_instance;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment