Commit 1c3dab29 authored by Christian Kamm's avatar Christian Kamm

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/2850Reviewed-by: default avatarLeandro T. C. Melo <leandro.melo@nokia.com>
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@nokia.com>
parent 56af8bfb
......@@ -184,15 +184,7 @@ void AnalyzerRunControl::addTask(Task::TaskType type, const QString &description
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
TaskHub *hub = pm->getObject<TaskHub>();
hub->addTask(Task(type, description, file, line, Constants::ANALYZERTASK_ID));
///FIXME: get a better API for this into Qt Creator
QList<Core::IOutputPane *> panes = pm->getObjects<Core::IOutputPane>();
foreach (Core::IOutputPane *pane, panes) {
if (pane->displayName() == tr("Build Issues")) {
pane->popup(false);
break;
}
}
hub->popup(false);
}
} // namespace Analyzer
......@@ -48,9 +48,9 @@ TaskHub::~TaskHub()
}
void TaskHub::addCategory(const QString &categoryId, const QString &displayName)
void TaskHub::addCategory(const QString &categoryId, const QString &displayName, bool visible)
{
emit categoryAdded(categoryId, displayName);
emit categoryAdded(categoryId, displayName, visible);
}
void TaskHub::addTask(const Task &task)
......@@ -68,6 +68,16 @@ void TaskHub::removeTask(const Task &task)
emit taskRemoved(task);
}
void TaskHub::setCategoryVisibility(const QString &categoryId, bool visible)
{
emit categoryVisibilityChanged(categoryId, visible);
}
void TaskHub::popup(bool withFocus)
{
emit popupRequested(withFocus);
}
QIcon TaskHub::taskTypeIcon(Task::TaskType t) const
{
switch (t) {
......
......@@ -48,18 +48,23 @@ public:
TaskHub();
virtual ~TaskHub();
void addCategory(const QString &categoryId, const QString &displayName);
void addCategory(const QString &categoryId, const QString &displayName, bool visible = true);
void addTask(const Task &task);
void clearTasks(const QString &categoryId = QString());
void removeTask(const Task &task);
void setCategoryVisibility(const QString &categoryId, bool visible);
void popup(bool withFocus);
// TODO now there are two places for icons
QIcon taskTypeIcon(ProjectExplorer::Task::TaskType t) const;
signals:
void categoryAdded(const QString &categoryId, const QString &displayName);
void categoryAdded(const QString &categoryId, const QString &displayName, bool visible);
void taskAdded(const ProjectExplorer::Task &task);
void taskRemoved(const ProjectExplorer::Task &task);
void tasksCleared(const QString &categoryId);
void categoryVisibilityChanged(const QString &categoryId, bool visible);
void popupRequested(bool withFocus);
private:
const QIcon m_errorIcon;
const QIcon m_warningIcon;
......
......@@ -679,14 +679,18 @@ TaskWindow::TaskWindow(TaskHub *taskhub) : d(new TaskWindowPrivate)
d->m_categoriesButton->setMenu(d->m_categoriesMenu);
connect(d->m_taskHub, SIGNAL(categoryAdded(QString, QString)),
this, SLOT(addCategory(QString, QString)));
connect(d->m_taskHub, SIGNAL(categoryAdded(QString, QString, bool)),
this, SLOT(addCategory(QString, QString, bool)));
connect(d->m_taskHub, SIGNAL(taskAdded(ProjectExplorer::Task)),
this, SLOT(addTask(ProjectExplorer::Task)));
connect(d->m_taskHub, SIGNAL(taskRemoved(ProjectExplorer::Task)),
this, SLOT(removeTask(ProjectExplorer::Task)));
connect(d->m_taskHub, SIGNAL(tasksCleared(QString)),
this, SLOT(clearTasks(QString)));
connect(d->m_taskHub, SIGNAL(categoryVisibilityChanged(QString,bool)),
this, SLOT(setCategoryVisibility(QString,bool)));
connect(d->m_taskHub, SIGNAL(popupRequested(bool)),
this, SLOT(popup(bool)));
}
TaskWindow::~TaskWindow()
......@@ -719,14 +723,35 @@ void TaskWindow::clearTasks(const QString &categoryId)
navigateStateChanged();
}
void TaskWindow::setCategoryVisibility(const QString &categoryId, bool visible)
{
if (categoryId.isEmpty())
return;
QStringList categories = d->m_filter->filteredCategories();
if (visible) {
categories.removeOne(categoryId);
} else {
categories.append(categoryId);
}
d->m_filter->setFilteredCategories(categories);
}
void TaskWindow::visibilityChanged(bool /* b */)
{
}
void TaskWindow::addCategory(const QString &categoryId, const QString &displayName)
void TaskWindow::addCategory(const QString &categoryId, const QString &displayName, bool visible)
{
Q_ASSERT(!categoryId.isEmpty());
d->m_model->addCategory(categoryId, displayName);
if (!visible) {
QStringList filters = d->m_filter->filteredCategories();
filters += categoryId;
d->m_filter->setFilteredCategories(filters);
}
}
void TaskWindow::addTask(const Task &task)
......@@ -848,16 +873,7 @@ void TaskWindow::filterCategoryTriggered(QAction *action)
QString categoryId = action->data().toString();
Q_ASSERT(!categoryId.isEmpty());
QStringList categories = d->m_filter->filteredCategories();
Q_ASSERT(d->m_filter->filteredCategories().contains(categoryId) == action->isChecked());
if (action->isChecked()) {
categories.removeOne(categoryId);
} else {
categories.append(categoryId);
}
d->m_filter->setFilteredCategories(categories);
setCategoryVisibility(categoryId, action->isChecked());
}
int TaskWindow::taskCount() const
......
......@@ -85,10 +85,11 @@ signals:
void tasksCleared();
private slots:
void addCategory(const QString &categoryId, const QString &displayName);
void addCategory(const QString &categoryId, const QString &displayName, bool visible);
void addTask(const ProjectExplorer::Task &task);
void removeTask(const ProjectExplorer::Task &task);
void clearTasks(const QString &categoryId);
void setCategoryVisibility(const QString &categoryId, bool visible);
void triggerDefaultHandler(const QModelIndex &index);
void showContextMenu(const QPoint &position);
......
......@@ -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)
{
QList<ProjectExplorer::Task> result;
foreach (const DiagnosticMessage &msg, messages) {
ProjectExplorer::Task::TaskType type
= msg.isError() ? ProjectExplorer::Task::Error
: ProjectExplorer::Task::Warning;
ProjectExplorer::Task task(type, msg.message, fileName, msg.loc.startLine,
category);
result += task;
}
return result;
}
void QmlTaskManager::collectMessages(
QFutureInterface<FileErrorMessages> &future,
Snapshot snapshot, QList<ModelManagerInterface::ProjectInfo> projectInfos,
QStringList importPaths, bool updateSemantic)
{
// ### 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);
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);
}
foreach (const QString &fileName, files) {
Document::Ptr document = snapshot.document(fileName);
if (!document)
continue;
foreach (const QString &fileName, info.sourceFiles) {
Document::Ptr document = snapshot.document(fileName);
if (!document)
continue;
FileErrorMessages result;
result.fileName = fileName;
result.messages = document->diagnosticMessages();
FileErrorMessages result;
result.fileName = fileName;
result.tasks = convertToTasks(document->diagnosticMessages(),
fileName, Constants::TASK_CATEGORY_QML);
//result.messages += linkMessages.value(fileName);
if (updateSemantic) {
result.tasks += convertToTasks(linkMessages.value(fileName),
fileName, Constants::TASK_CATEGORY_QML_ANALYSIS);
//Check checker(document, &ctx);
//result.messages.append(checker());
Check checker(document, context);
result.tasks += convertToTasks(checker(),
fileName, Constants::TASK_CATEGORY_QML_ANALYSIS);
}
future.reportResult(result);
if (future.isCanceled())
break;
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
......
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