forked from qt-creator/qt-creator
QmlJS: Show semantic messages on request.
* Add 'Run Checks' action (Ctrl-Shift-C) to perform checks on all projects. * Add 'QML Analysis' build issues category to separate clear errors from semantic analysis results. * Disabled automatic updating of analysis results. Change-Id: I4c948b1bd39f55655073e56c5e3ca7837f727665 Reviewed-on: http://codereview.qt.nokia.com/2850 Reviewed-by: Leandro T. C. Melo <leandro.melo@nokia.com> Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
@@ -53,12 +53,14 @@ const char * const TASK_SEARCH = "QmlJSEditor.TaskSearch";
|
||||
const char * const FOLLOW_SYMBOL_UNDER_CURSOR = "QmlJSEditor.FollowSymbolUnderCursor";
|
||||
const char * const FIND_USAGES = "QmlJSEditor.FindUsages";
|
||||
const char * const RENAME_USAGES = "QmlJSEditor.RenameUsages";
|
||||
const char * const RUN_SEMANTIC_SCAN = "QmlJSEditor.RunSemanticScan";
|
||||
const char * const SHOW_QT_QUICK_HELPER = "QmlJSEditor.ShowQtQuickHelper";
|
||||
|
||||
const char * const QML_MIMETYPE = "application/x-qml";
|
||||
const char * const JS_MIMETYPE = "application/javascript";
|
||||
|
||||
const char *const TASK_CATEGORY_QML = "Task.Category.Qml";
|
||||
const char *const TASK_CATEGORY_QML_ANALYSIS = "Task.Category.QmlAnalysis";
|
||||
|
||||
const char * const WIZARD_CATEGORY_QML = "S.Qml";
|
||||
const char * const WIZARD_TR_CATEGORY_QML = QT_TRANSLATE_NOOP("QmlJsEditor", "QML");
|
||||
|
||||
@@ -131,6 +131,20 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
|
||||
m_modelManager = QmlJS::ModelManagerInterface::instance();
|
||||
addAutoReleasedObject(new QmlJSSnippetProvider);
|
||||
|
||||
// QML task updating manager
|
||||
m_qmlTaskManager = new QmlTaskManager;
|
||||
addAutoReleasedObject(m_qmlTaskManager);
|
||||
connect(m_modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
// recompute messages when information about libraries changes
|
||||
connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
// recompute messages when project data changes (files added or removed)
|
||||
connect(m_modelManager, SIGNAL(projectInfoUpdated(ProjectInfo)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
connect(m_modelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
|
||||
m_qmlTaskManager, SLOT(documentsRemoved(QStringList)));
|
||||
|
||||
Core::Context context(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
|
||||
|
||||
m_editor = new QmlJSEditorFactory(this);
|
||||
@@ -187,6 +201,12 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
|
||||
contextMenu->addAction(cmd);
|
||||
qmlToolsMenu->addAction(cmd);
|
||||
|
||||
QAction *semanticScan = new QAction(tr("Run Checks"), this);
|
||||
cmd = am->registerAction(semanticScan, Core::Id(Constants::RUN_SEMANTIC_SCAN), globalContext);
|
||||
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+C")));
|
||||
connect(semanticScan, SIGNAL(triggered()), this, SLOT(runSemanticScan()));
|
||||
qmlToolsMenu->addAction(cmd);
|
||||
|
||||
QAction *showQuickToolbar = new QAction(tr("Show Qt Quick Toolbar"), this);
|
||||
cmd = am->registerAction(showQuickToolbar, Constants::SHOW_QT_QUICK_HELPER, context);
|
||||
#ifdef Q_WS_MACX
|
||||
@@ -227,20 +247,6 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
|
||||
|
||||
addAutoReleasedObject(new QmlJSOutlineWidgetFactory);
|
||||
|
||||
m_qmlTaskManager = new QmlTaskManager;
|
||||
addAutoReleasedObject(m_qmlTaskManager);
|
||||
|
||||
connect(m_modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
// recompute messages when information about libraries changes
|
||||
connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
// recompute messages when project data changes (files added or removed)
|
||||
connect(m_modelManager, SIGNAL(projectInfoUpdated(ProjectInfo)),
|
||||
m_qmlTaskManager, SLOT(updateMessages()));
|
||||
connect(m_modelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
|
||||
m_qmlTaskManager, SLOT(documentsRemoved(QStringList)));
|
||||
|
||||
addAutoReleasedObject(new QuickToolBar);
|
||||
addAutoReleasedObject(new Internal::QuickToolBarSettingsPage);
|
||||
|
||||
@@ -254,6 +260,7 @@ void QmlJSEditorPlugin::extensionsInitialized()
|
||||
ProjectExplorer::TaskHub *taskHub =
|
||||
ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
|
||||
taskHub->addCategory(Constants::TASK_CATEGORY_QML, tr("QML"));
|
||||
taskHub->addCategory(Constants::TASK_CATEGORY_QML_ANALYSIS, tr("QML Analysis"), false);
|
||||
}
|
||||
|
||||
ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
|
||||
@@ -330,4 +337,12 @@ void QmlJSEditorPlugin::currentEditorChanged(Core::IEditor *editor)
|
||||
}
|
||||
}
|
||||
|
||||
void QmlJSEditorPlugin::runSemanticScan()
|
||||
{
|
||||
m_qmlTaskManager->updateSemanticMessagesNow();
|
||||
ProjectExplorer::TaskHub *hub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
|
||||
hub->setCategoryVisibility(Constants::TASK_CATEGORY_QML_ANALYSIS, true);
|
||||
hub->popup(false);
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN(QmlJSEditorPlugin)
|
||||
|
||||
@@ -99,6 +99,7 @@ public Q_SLOTS:
|
||||
|
||||
private Q_SLOTS:
|
||||
void currentEditorChanged(Core::IEditor *editor);
|
||||
void runSemanticScan();
|
||||
|
||||
private:
|
||||
Core::Command *addToolAction(QAction *a, Core::ActionManager *am, Core::Context &context, const QString &name,
|
||||
|
||||
@@ -53,48 +53,76 @@ namespace QmlJSEditor {
|
||||
namespace Internal {
|
||||
|
||||
QmlTaskManager::QmlTaskManager(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_taskHub(0)
|
||||
QObject(parent),
|
||||
m_taskHub(0),
|
||||
m_updatingSemantic(false)
|
||||
{
|
||||
m_taskHub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
|
||||
|
||||
// displaying results incrementally leads to flickering
|
||||
// connect(&m_messageCollector, SIGNAL(resultsReadyAt(int,int)),
|
||||
// SLOT(displayResults(int,int)));
|
||||
connect(&m_messageCollector, SIGNAL(finished()),
|
||||
SLOT(displayAllResults()));
|
||||
|
||||
m_updateDelay.setInterval(100);
|
||||
m_updateDelay.setInterval(500);
|
||||
m_updateDelay.setSingleShot(true);
|
||||
connect(&m_updateDelay, SIGNAL(timeout()),
|
||||
SLOT(updateMessagesNow()));
|
||||
}
|
||||
|
||||
void QmlTaskManager::collectMessages(QFutureInterface<FileErrorMessages> &future,
|
||||
Snapshot snapshot, QStringList files, QStringList /*importPaths*/)
|
||||
static QList<ProjectExplorer::Task> convertToTasks(const QList<DiagnosticMessage> &messages, const QString &fileName, const QString &category)
|
||||
{
|
||||
// ### link and check error messages are disabled for now: too many false-positives!
|
||||
//Context ctx(snapshot);
|
||||
//QHash<QString, QList<DiagnosticMessage> > linkMessages;
|
||||
//Link link(&ctx, snapshot, importPaths);
|
||||
//link(&linkMessages);
|
||||
QList<ProjectExplorer::Task> result;
|
||||
foreach (const DiagnosticMessage &msg, messages) {
|
||||
ProjectExplorer::Task::TaskType type
|
||||
= msg.isError() ? ProjectExplorer::Task::Error
|
||||
: ProjectExplorer::Task::Warning;
|
||||
|
||||
foreach (const QString &fileName, files) {
|
||||
Document::Ptr document = snapshot.document(fileName);
|
||||
if (!document)
|
||||
continue;
|
||||
ProjectExplorer::Task task(type, msg.message, fileName, msg.loc.startLine,
|
||||
category);
|
||||
|
||||
FileErrorMessages result;
|
||||
result.fileName = fileName;
|
||||
result.messages = document->diagnosticMessages();
|
||||
result += task;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//result.messages += linkMessages.value(fileName);
|
||||
void QmlTaskManager::collectMessages(
|
||||
QFutureInterface<FileErrorMessages> &future,
|
||||
Snapshot snapshot, QList<ModelManagerInterface::ProjectInfo> projectInfos,
|
||||
QStringList importPaths, bool updateSemantic)
|
||||
{
|
||||
foreach (const ModelManagerInterface::ProjectInfo &info, projectInfos) {
|
||||
QHash<QString, QList<DiagnosticMessage> > linkMessages;
|
||||
ContextPtr context;
|
||||
if (updateSemantic) {
|
||||
Link link(snapshot, importPaths, snapshot.libraryInfo(info.qtImportsPath));
|
||||
context = link(&linkMessages);
|
||||
}
|
||||
|
||||
//Check checker(document, &ctx);
|
||||
//result.messages.append(checker());
|
||||
foreach (const QString &fileName, info.sourceFiles) {
|
||||
Document::Ptr document = snapshot.document(fileName);
|
||||
if (!document)
|
||||
continue;
|
||||
|
||||
future.reportResult(result);
|
||||
if (future.isCanceled())
|
||||
break;
|
||||
FileErrorMessages result;
|
||||
result.fileName = fileName;
|
||||
result.tasks = convertToTasks(document->diagnosticMessages(),
|
||||
fileName, Constants::TASK_CATEGORY_QML);
|
||||
|
||||
if (updateSemantic) {
|
||||
result.tasks += convertToTasks(linkMessages.value(fileName),
|
||||
fileName, Constants::TASK_CATEGORY_QML_ANALYSIS);
|
||||
|
||||
Check checker(document, context);
|
||||
result.tasks += convertToTasks(checker(),
|
||||
fileName, Constants::TASK_CATEGORY_QML_ANALYSIS);
|
||||
}
|
||||
|
||||
future.reportResult(result);
|
||||
if (future.isCanceled())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,24 +131,30 @@ void QmlTaskManager::updateMessages()
|
||||
m_updateDelay.start();
|
||||
}
|
||||
|
||||
void QmlTaskManager::updateMessagesNow()
|
||||
void QmlTaskManager::updateSemanticMessagesNow()
|
||||
{
|
||||
updateMessagesNow(true);
|
||||
}
|
||||
|
||||
void QmlTaskManager::updateMessagesNow(bool updateSemantic)
|
||||
{
|
||||
// don't restart a small update if a big one is running
|
||||
if (!updateSemantic && m_updatingSemantic)
|
||||
return;
|
||||
m_updatingSemantic = updateSemantic;
|
||||
|
||||
// abort any update that's going on already
|
||||
m_messageCollector.cancel();
|
||||
removeAllTasks();
|
||||
|
||||
// collect all the source files in open projects
|
||||
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
||||
QStringList sourceFiles;
|
||||
foreach (const ModelManagerInterface::ProjectInfo &info, modelManager->projectInfos()) {
|
||||
sourceFiles += info.sourceFiles;
|
||||
}
|
||||
|
||||
// process them
|
||||
QFuture<FileErrorMessages> future =
|
||||
QtConcurrent::run<FileErrorMessages>(
|
||||
&collectMessages, modelManager->snapshot(), sourceFiles,
|
||||
modelManager->importPaths());
|
||||
&collectMessages, modelManager->snapshot(), modelManager->projectInfos(),
|
||||
modelManager->importPaths(), !updateSemantic);
|
||||
m_messageCollector.setFuture(future);
|
||||
}
|
||||
|
||||
@@ -134,14 +168,7 @@ void QmlTaskManager::displayResults(int begin, int end)
|
||||
{
|
||||
for (int i = begin; i < end; ++i) {
|
||||
FileErrorMessages result = m_messageCollector.resultAt(i);
|
||||
foreach (const DiagnosticMessage &msg, result.messages) {
|
||||
ProjectExplorer::Task::TaskType type
|
||||
= msg.isError() ? ProjectExplorer::Task::Error
|
||||
: ProjectExplorer::Task::Warning;
|
||||
|
||||
ProjectExplorer::Task task(type, msg.message, result.fileName, msg.loc.startLine,
|
||||
Constants::TASK_CATEGORY_QML);
|
||||
|
||||
foreach (const ProjectExplorer::Task &task, result.tasks) {
|
||||
insertTask(task);
|
||||
}
|
||||
}
|
||||
@@ -150,6 +177,7 @@ void QmlTaskManager::displayResults(int begin, int end)
|
||||
void QmlTaskManager::displayAllResults()
|
||||
{
|
||||
displayResults(0, m_messageCollector.future().resultCount());
|
||||
m_updatingSemantic = false;
|
||||
}
|
||||
|
||||
void QmlTaskManager::insertTask(const ProjectExplorer::Task &task)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <projectexplorer/task.h>
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QList>
|
||||
@@ -64,12 +65,13 @@ public:
|
||||
|
||||
public slots:
|
||||
void updateMessages();
|
||||
void updateSemanticMessagesNow();
|
||||
void documentsRemoved(const QStringList path);
|
||||
|
||||
private slots:
|
||||
void displayResults(int begin, int end);
|
||||
void displayAllResults();
|
||||
void updateMessagesNow();
|
||||
void updateMessagesNow(bool updateSemantic = false);
|
||||
|
||||
private:
|
||||
void insertTask(const ProjectExplorer::Task &task);
|
||||
@@ -81,16 +83,20 @@ private:
|
||||
{
|
||||
public:
|
||||
QString fileName;
|
||||
QList<QmlJS::DiagnosticMessage> messages;
|
||||
QList<ProjectExplorer::Task> tasks;
|
||||
};
|
||||
static void collectMessages(QFutureInterface<FileErrorMessages> &future,
|
||||
QmlJS::Snapshot snapshot, QStringList files, QStringList importPaths);
|
||||
QmlJS::Snapshot snapshot,
|
||||
QList<QmlJS::ModelManagerInterface::ProjectInfo> projectInfos,
|
||||
QStringList importPaths,
|
||||
bool updateSemantic);
|
||||
|
||||
private:
|
||||
ProjectExplorer::TaskHub *m_taskHub;
|
||||
QMap<QString, QList<ProjectExplorer::Task> > m_docsWithTasks;
|
||||
QFutureWatcher<FileErrorMessages> m_messageCollector;
|
||||
QTimer m_updateDelay;
|
||||
bool m_updatingSemantic;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
|
||||
Reference in New Issue
Block a user