forked from qt-creator/qt-creator
Qml&Js: properties/methods/enums inspection
.. of builtIn qml and cpp code over "Inspect API For Element Under Cursor" action. Change-Id: I70d5bec2933b682295c5242248a2b0f95dba4e76 Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
@@ -137,40 +137,37 @@ public:
|
||||
} // end of anonymous namespace
|
||||
|
||||
namespace QmlJS {
|
||||
namespace Internal {
|
||||
class MetaFunction: public FunctionValue
|
||||
|
||||
MetaFunction::MetaFunction(const FakeMetaMethod &method, ValueOwner *valueOwner)
|
||||
: FunctionValue(valueOwner), m_method(method)
|
||||
{
|
||||
FakeMetaMethod m_method;
|
||||
}
|
||||
|
||||
public:
|
||||
MetaFunction(const FakeMetaMethod &method, ValueOwner *valueOwner)
|
||||
: FunctionValue(valueOwner), m_method(method)
|
||||
{
|
||||
}
|
||||
int MetaFunction::namedArgumentCount() const
|
||||
{
|
||||
return m_method.parameterNames().size();
|
||||
}
|
||||
|
||||
virtual int namedArgumentCount() const
|
||||
{
|
||||
return m_method.parameterNames().size();
|
||||
}
|
||||
QString MetaFunction::argumentName(int index) const
|
||||
{
|
||||
if (index < m_method.parameterNames().size())
|
||||
return m_method.parameterNames().at(index);
|
||||
|
||||
virtual QString argumentName(int index) const
|
||||
{
|
||||
if (index < m_method.parameterNames().size())
|
||||
return m_method.parameterNames().at(index);
|
||||
return FunctionValue::argumentName(index);
|
||||
}
|
||||
|
||||
return FunctionValue::argumentName(index);
|
||||
}
|
||||
|
||||
virtual bool isVariadic() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const MetaFunction *asMetaFunction() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
} // namespace Internal
|
||||
bool MetaFunction::isVariadic() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const MetaFunction *MetaFunction::asMetaFunction() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
const FakeMetaMethod &MetaFunction::fakeMetaMethod() const
|
||||
{
|
||||
return m_method;
|
||||
}
|
||||
|
||||
FakeMetaObjectWithOrigin::FakeMetaObjectWithOrigin(FakeMetaObject::ConstPtr fakeMetaObject, const QString &originId)
|
||||
: fakeMetaObject(fakeMetaObject)
|
||||
@@ -293,7 +290,7 @@ void CppComponentValue::processMembers(MemberProcessor *processor) const
|
||||
signatures = new QList<const Value *>;
|
||||
signatures->reserve(m_metaObject->methodCount());
|
||||
for (int index = 0; index < m_metaObject->methodCount(); ++index)
|
||||
signatures->append(new Internal::MetaFunction(m_metaObject->method(index), valueOwner()));
|
||||
signatures->append(new MetaFunction(m_metaObject->method(index), valueOwner()));
|
||||
if (!m_metaSignatures.testAndSetOrdered(0, signatures)) {
|
||||
delete signatures;
|
||||
signatures = m_metaSignatures.load();
|
||||
@@ -830,7 +827,7 @@ const Function *Value::asFunction() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Internal::MetaFunction *Value::asMetaFunction() const
|
||||
const MetaFunction *Value::asMetaFunction() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@@ -83,10 +83,10 @@ class UnknownValue;
|
||||
class UrlValue;
|
||||
class Value;
|
||||
class ValueOwner;
|
||||
class MetaFunction;
|
||||
typedef QSharedPointer<const Context> ContextPtr;
|
||||
|
||||
namespace Internal {
|
||||
class MetaFunction;
|
||||
class QtObjectPrototypeReference;
|
||||
} // namespace Internal
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
virtual const ASTSignal *asAstSignal() const;
|
||||
virtual const ASTFunctionValue *asAstFunctionValue() const;
|
||||
virtual const Function *asFunction() const;
|
||||
virtual const Internal::MetaFunction *asMetaFunction() const;
|
||||
virtual const MetaFunction *asMetaFunction() const;
|
||||
virtual const JSImportScope *asJSImportScope() const;
|
||||
virtual const TypeScope *asTypeScope() const;
|
||||
|
||||
@@ -304,7 +304,7 @@ template <> Q_INLINE_TEMPLATE const Function *value_cast(const Value *v)
|
||||
else return 0;
|
||||
}
|
||||
|
||||
template <> Q_INLINE_TEMPLATE const Internal::MetaFunction*value_cast(const Value *v)
|
||||
template <> Q_INLINE_TEMPLATE const MetaFunction *value_cast(const Value *v)
|
||||
{
|
||||
if (v) return v->asMetaFunction();
|
||||
else return 0;
|
||||
@@ -1110,6 +1110,20 @@ private:
|
||||
bool m_importFailed;
|
||||
};
|
||||
|
||||
class QMLJS_EXPORT MetaFunction: public FunctionValue
|
||||
{
|
||||
LanguageUtils::FakeMetaMethod m_method;
|
||||
|
||||
public:
|
||||
MetaFunction(const LanguageUtils::FakeMetaMethod &method, ValueOwner *valueOwner);
|
||||
|
||||
int namedArgumentCount() const override;
|
||||
QString argumentName(int index) const override;
|
||||
bool isVariadic() const override;
|
||||
const MetaFunction *asMetaFunction() const override;
|
||||
const LanguageUtils::FakeMetaMethod &fakeMetaMethod() const;
|
||||
};
|
||||
|
||||
} // namespace QmlJS
|
||||
|
||||
#endif // QMLJS_INTERPRETER_H
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include "qmljseditordocument.h"
|
||||
#include "qmljseditorplugin.h"
|
||||
#include "qmljsfindreferences.h"
|
||||
#include "qmljshighlighter.h"
|
||||
#include "qmljshoverhandler.h"
|
||||
#include "qmljsquickfixassist.h"
|
||||
#include "qmloutlinemodel.h"
|
||||
@@ -72,10 +73,10 @@
|
||||
#include <texteditor/codeassist/genericproposalmodel.h>
|
||||
#include <texteditor/texteditoractionhandler.h>
|
||||
|
||||
#include <utils/changeset.h>
|
||||
#include <utils/uncommentselection.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/annotateditemdelegate.h>
|
||||
#include <utils/changeset.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/uncommentselection.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
@@ -556,6 +557,166 @@ void QmlJSEditorWidget::createToolBar()
|
||||
insertExtraToolBarWidget(TextEditorWidget::Left, m_outlineCombo);
|
||||
}
|
||||
|
||||
class CodeModelInspector : public MemberProcessor
|
||||
{
|
||||
public:
|
||||
explicit CodeModelInspector(const CppComponentValue *processingValue, QTextStream *stream) :
|
||||
m_processingValue(processingValue),
|
||||
m_stream(stream),
|
||||
m_indent(QLatin1String(" "))
|
||||
{
|
||||
}
|
||||
|
||||
bool processProperty(const QString &name, const Value *value,
|
||||
const PropertyInfo &propertyInfo) override
|
||||
{
|
||||
QString type;
|
||||
if (const CppComponentValue *cpp = value->asCppComponentValue())
|
||||
type = cpp->metaObject()->className();
|
||||
else
|
||||
type = m_processingValue->propertyType(name);
|
||||
|
||||
if (propertyInfo.isList())
|
||||
type = QStringLiteral("list<%1>").arg(type);
|
||||
|
||||
*m_stream << m_indent;
|
||||
if (!propertyInfo.isWriteable())
|
||||
*m_stream << "readonly ";
|
||||
*m_stream << "property " << type << " " << name << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool processSignal(const QString &name, const Value *value) override
|
||||
{
|
||||
*m_stream << m_indent << "signal " << name << stringifyFunctionParameters(value) << endl;
|
||||
return true;
|
||||
}
|
||||
bool processSlot(const QString &name, const Value *value) override
|
||||
{
|
||||
*m_stream << m_indent << "function " << name << stringifyFunctionParameters(value) << endl;
|
||||
return true;
|
||||
}
|
||||
bool processGeneratedSlot(const QString &name, const Value *value) override
|
||||
{
|
||||
*m_stream << m_indent << "/*generated*/ function " << name
|
||||
<< stringifyFunctionParameters(value) << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QString stringifyFunctionParameters(const Value *value) const
|
||||
{
|
||||
QStringList params;
|
||||
const QmlJS::MetaFunction *metaFunction = value->asMetaFunction();
|
||||
if (metaFunction) {
|
||||
QStringList paramNames = metaFunction->fakeMetaMethod().parameterNames();
|
||||
QStringList paramTypes = metaFunction->fakeMetaMethod().parameterTypes();
|
||||
for (int i = 0; i < paramTypes.size(); ++i) {
|
||||
QString typeAndNamePair = paramTypes.at(i);
|
||||
if (paramNames.size() > i) {
|
||||
QString paramName = paramNames.at(i);
|
||||
if (!paramName.isEmpty())
|
||||
typeAndNamePair += QLatin1Char(' ') + paramName;
|
||||
}
|
||||
params.append(typeAndNamePair);
|
||||
}
|
||||
}
|
||||
return QLatin1Char('(') + params.join(QLatin1String(", ")) + QLatin1Char(')');
|
||||
}
|
||||
|
||||
private:
|
||||
const CppComponentValue *m_processingValue;
|
||||
QTextStream *m_stream;
|
||||
const QString m_indent;
|
||||
};
|
||||
|
||||
static const CppComponentValue *findCppComponentToInspect(const SemanticInfo &semanticInfo,
|
||||
const unsigned cursorPosition)
|
||||
{
|
||||
AST::Node *node = semanticInfo.astNodeAt(cursorPosition);
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
const ScopeChain scopeChain = semanticInfo.scopeChain(semanticInfo.rangePath(cursorPosition));
|
||||
Evaluate evaluator(&scopeChain);
|
||||
const Value *value = evaluator.reference(node);
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
return value->asCppComponentValue();
|
||||
}
|
||||
|
||||
static QString inspectCppComponent(const CppComponentValue *cppValue)
|
||||
{
|
||||
QString result;
|
||||
QTextStream bufWriter(&result);
|
||||
|
||||
// for QtObject
|
||||
QString superClassName = cppValue->metaObject()->superclassName();
|
||||
if (superClassName.isEmpty())
|
||||
superClassName = cppValue->metaObject()->className();
|
||||
|
||||
bufWriter << "import QtQuick " << cppValue->importVersion().toString() << endl
|
||||
<< "// " << cppValue->metaObject()->className()
|
||||
<< " imported as " << cppValue->moduleName() << " "
|
||||
<< cppValue->importVersion().toString() << endl
|
||||
<< endl
|
||||
<< superClassName << " {" << endl;
|
||||
|
||||
CodeModelInspector insp(cppValue, &bufWriter);
|
||||
cppValue->processMembers(&insp);
|
||||
|
||||
bufWriter << endl;
|
||||
const int enumeratorCount = cppValue->metaObject()->enumeratorCount();
|
||||
for (int index = cppValue->metaObject()->enumeratorOffset(); index < enumeratorCount; ++index) {
|
||||
LanguageUtils::FakeMetaEnum enumerator = cppValue->metaObject()->enumerator(index);
|
||||
bufWriter << " // Enum " << enumerator.name() << " { " <<
|
||||
enumerator.keys().join(QLatin1Char(',')) << " }" << endl;
|
||||
}
|
||||
|
||||
bufWriter << "}" << endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
void QmlJSEditorWidget::inspectElementUnderCursor() const
|
||||
{
|
||||
const QTextCursor cursor = textCursor();
|
||||
|
||||
const unsigned cursorPosition = cursor.position();
|
||||
const SemanticInfo semanticInfo = m_qmlJsEditorDocument->semanticInfo();
|
||||
if (!semanticInfo.isValid())
|
||||
return;
|
||||
|
||||
const CppComponentValue *cppValue = findCppComponentToInspect(semanticInfo, cursorPosition);
|
||||
if (!cppValue) {
|
||||
QString title = tr("Code Model Not Available");
|
||||
const QString nothingToShow = QStringLiteral("nothingToShow");
|
||||
EditorManager::openEditorWithContents(Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, &title,
|
||||
tr("Code model not available.").toUtf8(), nothingToShow,
|
||||
EditorManager::IgnoreNavigationHistory);
|
||||
return;
|
||||
}
|
||||
|
||||
QString title = tr("Code Model of %1").arg(cppValue->metaObject()->className());
|
||||
IEditor *outputEditor = EditorManager::openEditorWithContents(
|
||||
Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, &title, QByteArray(),
|
||||
cppValue->metaObject()->className(), EditorManager::IgnoreNavigationHistory);
|
||||
|
||||
if (!outputEditor)
|
||||
return;
|
||||
|
||||
auto widget = qobject_cast<TextEditor::TextEditorWidget *>(outputEditor->widget());
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
widget->setReadOnly(true);
|
||||
widget->textDocument()->setTemporary(true);
|
||||
widget->textDocument()->setSyntaxHighlighter(new QmlJSHighlighter(widget->document()));
|
||||
|
||||
const QString buf = inspectCppComponent(cppValue);
|
||||
widget->textDocument()->setPlainText(buf);
|
||||
}
|
||||
|
||||
TextEditorWidget::Link QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor,
|
||||
bool /*resolveTarget*/,
|
||||
bool /*inNextSplit*/)
|
||||
|
@@ -75,6 +75,9 @@ public:
|
||||
|
||||
TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind,
|
||||
TextEditor::AssistReason reason) const;
|
||||
|
||||
void inspectElementUnderCursor() const;
|
||||
|
||||
public slots:
|
||||
void findUsages();
|
||||
void renameUsages();
|
||||
|
@@ -51,6 +51,7 @@ const char RENAME_USAGES[] = "QmlJSEditor.RenameUsages";
|
||||
const char RUN_SEMANTIC_SCAN[] = "QmlJSEditor.RunSemanticScan";
|
||||
const char REFORMAT_FILE[] = "QmlJSEditor.ReformatFile";
|
||||
const char SHOW_QT_QUICK_HELPER[] = "QmlJSEditor.ShowQtQuickHelper";
|
||||
const char INSPECT_ELEMENT_UNDER_CURSOR[] = "QmlJSEditor.InspectElementUnderCursor";
|
||||
|
||||
const char TASK_CATEGORY_QML[] = "Task.Category.Qml";
|
||||
const char TASK_CATEGORY_QML_ANALYSIS[] = "Task.Category.QmlAnalysis";
|
||||
|
@@ -158,6 +158,15 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
|
||||
connect(m_reformatFileAction, SIGNAL(triggered()), this, SLOT(reformatFile()));
|
||||
qmlToolsMenu->addAction(cmd);
|
||||
|
||||
QAction *inspectElementAction = new QAction(tr("Inspect API for Element Under Cursor"), this);
|
||||
cmd = ActionManager::registerAction(inspectElementAction,
|
||||
Id(Constants::INSPECT_ELEMENT_UNDER_CURSOR), context);
|
||||
connect(inspectElementAction, &QAction::triggered, [] {
|
||||
if (auto widget = qobject_cast<QmlJSEditorWidget *>(EditorManager::currentEditor()->widget()))
|
||||
widget->inspectElementUnderCursor();
|
||||
});
|
||||
qmlToolsMenu->addAction(cmd);
|
||||
|
||||
QAction *showQuickToolbar = new QAction(tr("Show Qt Quick Toolbar"), this);
|
||||
cmd = ActionManager::registerAction(showQuickToolbar, Constants::SHOW_QT_QUICK_HELPER, context);
|
||||
cmd->setDefaultKeySequence(UseMacShortcuts ? QKeySequence(Qt::META + Qt::ALT + Qt::Key_Space)
|
||||
|
Reference in New Issue
Block a user