Commit 967ed09c authored by Roberto Raggi's avatar Roberto Raggi
Browse files

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

parent b02d2f9f
......@@ -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);
......
......@@ -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();
}
......
......@@ -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);
......
......@@ -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();
}
......@@ -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
......
......@@ -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();
......
......@@ -135,6 +135,7 @@ public:
int documentRevision() const;
public slots:
void followSymbolUnderCursor();
virtual void setFontSettings(const TextEditor::FontSettings &);
private slots:
......
......@@ -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";
......
......@@ -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)
......@@ -64,6 +64,9 @@ public:
void initializeEditor(QmlJSTextEditor *editor);
public Q_SLOTS:
void followSymbolUnderCursor();
private:
static QmlJSEditorPlugin *m_instance;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment