Commit a429ef3d authored by Denis Kormalev's avatar Denis Kormalev

TODO plugin: Add file patterns to exclude from parsing

Additional list of regular expressions added to TODO plugin settings
to allow set patterns to be excluded from file list to parse by this plugin.

Change-Id: I718f111ac7592557a6aa86865283468c53d58078
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@theqtcompany.com>
parent 4f5a02d5
......@@ -54,6 +54,8 @@ const char ITEMS_DISPLAY_PLACE[] = "ItemsDisplayPlace";
const char KEYWORDS_LIST[] = "Keywords";
const char OUTPUT_PANE_TEXT_WIDTH[] = "OutputPaneTextColumnWidth";
const char OUTPUT_PANE_FILE_WIDTH[] = "OutputPaneFileColumnWidth";
const char SETTINGS_NAME_KEY[] = "TodoProjectSettings";
const char EXCLUDES_LIST_KEY[] = "ExcludesList";
// TODO Output TreeWidget columns
enum OutputColumnIndex {
......
......@@ -46,9 +46,11 @@ CppTodoItemsScanner::CppTodoItemsScanner(const KeywordList &keywordList, QObject
connect(modelManager, &CppTools::CppModelManager::documentUpdated,
this, &CppTodoItemsScanner::documentUpdated, Qt::DirectConnection);
setParams(keywordList);
}
void CppTodoItemsScanner::keywordListChanged()
void CppTodoItemsScanner::scannerParamsChanged()
{
// We need to rescan everything known to the code model
// TODO: It would be nice to only tokenize the source files, not update the code model entirely.
......@@ -72,7 +74,6 @@ void CppTodoItemsScanner::documentUpdated(CPlusPlus::Document::Ptr doc)
void CppTodoItemsScanner::processDocument(CPlusPlus::Document::Ptr doc)
{
QList<TodoItem> itemList;
CPlusPlus::TranslationUnit *translationUnit = doc->translationUnit();
for (unsigned i = 0; i < translationUnit->commentCount(); ++i) {
......
......@@ -47,7 +47,7 @@ public:
explicit CppTodoItemsScanner(const KeywordList &keywordList, QObject *parent = 0);
protected:
void keywordListChanged();
void scannerParamsChanged();
private slots:
void documentUpdated(CPlusPlus::Document::Ptr doc);
......
......@@ -44,12 +44,12 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
ui(new Ui::OptionsDialog)
{
ui->setupUi(this);
setButtonsEnabled();
connect(ui->addButton, SIGNAL(clicked()), SLOT(addButtonClicked()));
connect(ui->removeButton, SIGNAL(clicked()), SLOT(removeButtonClicked()));
connect(ui->editButton, SIGNAL(clicked()), SLOT(editButtonClicked()));
connect(ui->resetButton, SIGNAL(clicked()), SLOT(resetButtonClicked()));
connect(ui->keywordsList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(itemDoubleClicked(QListWidgetItem*)));
setKeywordsButtonsEnabled();
connect(ui->addKeywordButton, SIGNAL(clicked()), SLOT(addKeywordButtonClicked()));
connect(ui->removeKeywordButton, SIGNAL(clicked()), SLOT(removeKeywordButtonClicked()));
connect(ui->editKeywordButton, SIGNAL(clicked()), SLOT(editKeywordButtonClicked()));
connect(ui->resetKeywordsButton, SIGNAL(clicked()), SLOT(resetKeywordsButtonClicked()));
connect(ui->keywordsList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(keywordDoubleClicked(QListWidgetItem*)));
}
OptionsDialog::~OptionsDialog()
......@@ -57,9 +57,9 @@ OptionsDialog::~OptionsDialog()
delete ui;
}
void OptionsDialog::itemDoubleClicked(QListWidgetItem *item)
void OptionsDialog::keywordDoubleClicked(QListWidgetItem *item)
{
editItem(item);
editKeyword(item);
}
void OptionsDialog::setSettings(const Settings &settings)
......@@ -91,7 +91,7 @@ Settings OptionsDialog::settings()
return settingsFromUi();
}
void OptionsDialog::addButtonClicked()
void OptionsDialog::addKeywordButtonClicked()
{
Keyword keyword;
KeywordDialog *keywordDialog = new KeywordDialog(keyword, keywordNames(), this);
......@@ -101,13 +101,13 @@ void OptionsDialog::addButtonClicked()
}
}
void OptionsDialog::editButtonClicked()
void OptionsDialog::editKeywordButtonClicked()
{
QListWidgetItem *item = ui->keywordsList->currentItem();
editItem(item);
editKeyword(item);
}
void OptionsDialog::editItem(QListWidgetItem *item)
void OptionsDialog::editKeyword(QListWidgetItem *item)
{
Keyword keyword;
keyword.name = item->text();
......@@ -127,58 +127,58 @@ void OptionsDialog::editItem(QListWidgetItem *item)
}
}
void OptionsDialog::removeButtonClicked()
void OptionsDialog::removeKeywordButtonClicked()
{
ui->keywordsList->takeItem(ui->keywordsList->currentRow());
delete ui->keywordsList->takeItem(ui->keywordsList->currentRow());
}
void OptionsDialog::resetButtonClicked()
void OptionsDialog::resetKeywordsButtonClicked()
{
Settings newSettings;
newSettings.setDefault();
uiFromSettings(newSettings);
}
void OptionsDialog::setButtonsEnabled()
void OptionsDialog::setKeywordsButtonsEnabled()
{
bool isSomethingSelected = ui->keywordsList->selectedItems().count() != 0;
ui->removeButton->setEnabled(isSomethingSelected);
ui->editButton->setEnabled(isSomethingSelected);
ui->removeKeywordButton->setEnabled(isSomethingSelected);
ui->editKeywordButton->setEnabled(isSomethingSelected);
}
void OptionsDialog::uiFromSettings(const Settings &settings)
{
ui->scanInCurrentFileRadioButton->setChecked(settings.scanningScope == ScanningScopeCurrentFile);
ui->scanInProjectRadioButton->setChecked(settings.scanningScope == ScanningScopeProject);
void OptionsDialog::uiFromSettings(const Settings &settings)
{
ui->scanInCurrentFileRadioButton->setChecked(settings.scanningScope == ScanningScopeCurrentFile);
ui->scanInProjectRadioButton->setChecked(settings.scanningScope == ScanningScopeProject);
ui->keywordsList->clear();
foreach (const Keyword &keyword, settings.keywords)
addToKeywordsList(keyword);
}
ui->keywordsList->clear();
foreach (const Keyword &keyword, settings.keywords)
addToKeywordsList(keyword);
}
Settings OptionsDialog::settingsFromUi()
{
Settings settings;
Settings OptionsDialog::settingsFromUi()
{
Settings settings;
if (ui->scanInCurrentFileRadioButton->isChecked())
settings.scanningScope = ScanningScopeCurrentFile;
else
settings.scanningScope = ScanningScopeProject;
if (ui->scanInCurrentFileRadioButton->isChecked())
settings.scanningScope = ScanningScopeCurrentFile;
else
settings.scanningScope = ScanningScopeProject;
settings.keywords.clear();
for (int i = 0; i < ui->keywordsList->count(); ++i) {
QListWidgetItem *item = ui->keywordsList->item(i);
settings.keywords.clear();
for (int i = 0; i < ui->keywordsList->count(); ++i) {
QListWidgetItem *item = ui->keywordsList->item(i);
Keyword keyword;
keyword.name = item->text();
keyword.iconResource = item->data(Qt::UserRole).toString();
keyword.color = item->backgroundColor();
Keyword keyword;
keyword.name = item->text();
keyword.iconResource = item->data(Qt::UserRole).toString();
keyword.color = item->backgroundColor();
settings.keywords << keyword;
}
settings.keywords << keyword;
}
return settings;
}
return settings;
}
} // namespace Internal
} // namespace Todo
......@@ -57,18 +57,18 @@ public:
Settings settings();
private slots:
void addButtonClicked();
void editButtonClicked();
void removeButtonClicked();
void resetButtonClicked();
void setButtonsEnabled();
void itemDoubleClicked(QListWidgetItem *item);
void addKeywordButtonClicked();
void editKeywordButtonClicked();
void removeKeywordButtonClicked();
void resetKeywordsButtonClicked();
void setKeywordsButtonsEnabled();
void keywordDoubleClicked(QListWidgetItem *item);
private:
void uiFromSettings(const Settings &settings);
Settings settingsFromUi();
void addToKeywordsList(const Keyword &keyword);
void editItem(QListWidgetItem *item);
void editKeyword(QListWidgetItem *item);
QSet<QString> keywordNames();
Ui::OptionsDialog *ui;
......
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>377</width>
<height>299</height>
<width>444</width>
<height>482</height>
</rect>
</property>
<property name="windowTitle">
......@@ -30,28 +30,28 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="addButton">
<widget class="QPushButton" name="addKeywordButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editButton">
<widget class="QPushButton" name="editKeywordButton">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeButton">
<widget class="QPushButton" name="removeKeywordButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<widget class="QPushButton" name="resetKeywordsButton">
<property name="text">
<string>Reset</string>
</property>
......@@ -112,7 +112,7 @@
<sender>keywordsList</sender>
<signal>itemSelectionChanged()</signal>
<receiver>Todo::Internal::OptionsDialog</receiver>
<slot>setButtonsEnabled()</slot>
<slot>setKeywordsButtonsEnabled()</slot>
<hints>
<hint type="sourcelabel">
<x>247</x>
......@@ -126,6 +126,6 @@
</connection>
</connections>
<slots>
<slot>setButtonsEnabled()</slot>
<slot>setKeywordsButtonsEnabled()</slot>
</slots>
</ui>
......@@ -44,19 +44,22 @@ QmlJsTodoItemsScanner::QmlJsTodoItemsScanner(const KeywordList &keywordList, QOb
QmlJS::ModelManagerInterface *model = QmlJS::ModelManagerInterface::instance();
connect(model, &QmlJS::ModelManagerInterface::documentUpdated,
this, &QmlJsTodoItemsScanner::documentUpdated, Qt::DirectConnection);
setParams(keywordList);
}
bool QmlJsTodoItemsScanner::shouldProcessFile(const QString &fileName)
{
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
foreach (const QmlJS::ModelManagerInterface::ProjectInfo &info, modelManager->projectInfos())
foreach (const QmlJS::ModelManagerInterface::ProjectInfo &info, modelManager->projectInfos()) {
if (info.sourceFiles.contains(fileName))
return true;
}
return false;
}
void QmlJsTodoItemsScanner::keywordListChanged()
void QmlJsTodoItemsScanner::scannerParamsChanged()
{
// We need to rescan everything known to the code model
// TODO: It would be nice to only tokenize the source files, not update the code model entirely.
......@@ -81,7 +84,6 @@ void QmlJsTodoItemsScanner::processDocument(QmlJS::Document::Ptr doc)
QList<TodoItem> itemList;
foreach (const QmlJS::AST::SourceLocation &sourceLocation, doc->engine()->comments()) {
QString source = doc->source().mid(sourceLocation.begin(), sourceLocation.length).trimmed();
// Process every line
......
......@@ -48,7 +48,7 @@ public:
protected:
bool shouldProcessFile(const QString &fileName);
void keywordListChanged();
void scannerParamsChanged() override;
private slots:
void documentUpdated(QmlJS::Document::Ptr doc);
......
......@@ -72,12 +72,12 @@ void Settings::load(QSettings *settings)
scanningScope).toInt());
KeywordList newKeywords;
const int size = settings->beginReadArray(QLatin1String(Constants::KEYWORDS_LIST));
if (size > 0) {
const int keywordsSize = settings->beginReadArray(QLatin1String(Constants::KEYWORDS_LIST));
if (keywordsSize > 0) {
const QString nameKey = QLatin1String("name");
const QString colorKey = QLatin1String("color");
const QString iconResourceKey = QLatin1String("iconResource");
for (int i = 0; i < size; ++i) {
for (int i = 0; i < keywordsSize; ++i) {
settings->setArrayIndex(i);
Keyword keyword;
keyword.name = settings->value(nameKey).toString();
......@@ -129,8 +129,7 @@ void Settings::setDefault()
bool Settings::equals(const Settings &other) const
{
return (keywords == other.keywords)
&& (scanningScope == other.scanningScope);
&& (scanningScope == other.scanningScope);
}
bool operator ==(Settings &s1, Settings &s2)
......
......@@ -16,7 +16,9 @@ HEADERS += todoplugin.h \
qmljstodoitemsscanner.h \
lineparser.h \
todooutputtreeview.h \
todooutputtreeviewdelegate.h
todooutputtreeviewdelegate.h \
todoprojectsettingswidget.h
SOURCES += todoplugin.cpp \
keyword.cpp \
todooutputpane.cpp \
......@@ -31,11 +33,13 @@ SOURCES += todoplugin.cpp \
qmljstodoitemsscanner.cpp \
lineparser.cpp \
todooutputtreeview.cpp \
todooutputtreeviewdelegate.cpp
todooutputtreeviewdelegate.cpp \
todoprojectsettingswidget.cpp
RESOURCES += \
todoplugin.qrc
FORMS += \
optionsdialog.ui \
keyworddialog.ui
keyworddialog.ui \
todoprojectsettingswidget.ui
......@@ -26,6 +26,9 @@ QtcPlugin {
"optionsdialog.cpp",
"optionsdialog.h",
"optionsdialog.ui",
"todoprojectsettingswidget.cpp",
"todoprojectsettingswidget.h",
"todoprojectsettingswidget.ui",
"optionspage.cpp",
"optionspage.h",
"qmljstodoitemsscanner.cpp",
......
......@@ -66,15 +66,22 @@ TodoItemsModel *TodoItemsProvider::todoItemsModel()
void TodoItemsProvider::settingsChanged(const Settings &newSettings)
{
if (newSettings.keywords != m_settings.keywords)
if (newSettings.keywords != m_settings.keywords) {
foreach (TodoItemsScanner *scanner, m_scanners)
scanner->setKeywordList(newSettings.keywords);
scanner->setParams(newSettings.keywords);
}
m_settings = newSettings;
updateList();
}
void TodoItemsProvider::projectSettingsChanged(Project *project)
{
Q_UNUSED(project);
updateList();
}
void TodoItemsProvider::updateList()
{
m_itemsList.clear();
......@@ -84,9 +91,8 @@ void TodoItemsProvider::updateList()
if (m_currentEditor)
m_itemsList = m_itemsHash.value(m_currentEditor->document()->filePath().toString());
// Show only items of the startup project if any
} else {
if (m_startupProject)
setItemsListWithinStartupProject();
} else if (m_startupProject) {
setItemsListWithinStartupProject();
}
m_itemsModel->todoItemsListUpdated();
......@@ -102,19 +108,34 @@ void TodoItemsProvider::createScanners()
if (QmlJS::ModelManagerInterface::instance())
m_scanners << new QmlJsTodoItemsScanner(m_settings.keywords, this);
foreach (TodoItemsScanner *scanner, m_scanners)
foreach (TodoItemsScanner *scanner, m_scanners) {
connect(scanner, SIGNAL(itemsFetched(QString,QList<TodoItem>)), this,
SLOT(itemsFetched(QString,QList<TodoItem>)), Qt::QueuedConnection);
}
}
void TodoItemsProvider::setItemsListWithinStartupProject()
{
QHashIterator<QString, QList<TodoItem> > it(m_itemsHash);
QSet<QString> fileNames = QSet<QString>::fromList(m_startupProject->files(Project::ExcludeGeneratedFiles));
QVariantMap settings = m_startupProject->namedSettings(QLatin1String(Constants::SETTINGS_NAME_KEY)).toMap();
while (it.hasNext()) {
it.next();
if (fileNames.contains(it.key()))
m_itemsList << it.value();
QString fileName = it.key();
if (fileNames.contains(fileName)) {
bool skip = false;
for (const QVariant &pattern : settings[QLatin1String(Constants::EXCLUDES_LIST_KEY)].toList()) {
QRegExp re(pattern.toString());
if (re.indexIn(fileName) != -1) {
skip = true;
break;
}
}
if (!skip)
m_itemsList << it.value();
}
}
}
......
......@@ -57,6 +57,7 @@ public:
public slots:
void settingsChanged(const Settings &newSettings);
void projectSettingsChanged(ProjectExplorer::Project *project);
signals:
void itemsUpdated();
......
......@@ -39,25 +39,20 @@ namespace Todo {
namespace Internal {
TodoItemsScanner::TodoItemsScanner(const KeywordList &keywordList, QObject *parent) :
QObject(parent)
QObject(parent), m_keywordList(keywordList)
{
setKeywordList(keywordList);
}
void TodoItemsScanner::setKeywordList(const KeywordList &keywordList)
void TodoItemsScanner::setParams(const KeywordList &keywordList)
{
m_keywordList = keywordList;
keywordListChanged();
scannerParamsChanged();
}
// Descendants can override and make a request for full rescan here if needed
void TodoItemsScanner::keywordListChanged()
{
}
// Descendants can use this to process comment lines
void TodoItemsScanner::processCommentLine(const QString &fileName, const QString &comment,
unsigned lineNumber, QList<TodoItem> &outItemList)
unsigned lineNumber, QList<TodoItem> &outItemList)
{
LineParser parser(m_keywordList);
QList<TodoItem> newItemList = parser.parse(comment);
......
......@@ -37,6 +37,10 @@
#include <QObject>
namespace ProjectExplorer {
class Project;
}
namespace Todo {
namespace Internal {
......@@ -48,7 +52,7 @@ class TodoItemsScanner : public QObject
public:
explicit TodoItemsScanner(const KeywordList &keywordList, QObject *parent = 0);
void setKeywordList(const KeywordList &keywordList);
void setParams(const KeywordList &keywordList);
signals:
void itemsFetched(const QString &fileName, const QList<TodoItem> &items);
......@@ -56,9 +60,11 @@ signals:
protected:
KeywordList m_keywordList;
virtual void keywordListChanged();
void processCommentLine(const QString &fileName, const QString &comment, unsigned lineNumber,
QList<TodoItem> &outItemList);
// Descendants can override and make a request for full rescan here if needed
virtual void scannerParamsChanged() = 0;
void processCommentLine(const QString &fileName, const QString &comment,
unsigned lineNumber, QList<TodoItem> &outItemList);
};
}
......
......@@ -35,10 +35,12 @@
#include "keyword.h"
#include "todooutputpane.h"
#include "todoitemsprovider.h"
#include "todoprojectsettingswidget.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <projectexplorer/projectpanelfactory.h>
#include <QtPlugin>
#include <QFileInfo>
......@@ -71,6 +73,23 @@ bool TodoPlugin::initialize(const QStringList& args, QString *errMsg)
createItemsProvider();
createTodoOutputPane();
auto panelFactory = new ProjectExplorer::ProjectPanelFactory();
panelFactory->setPriority(100);
panelFactory->setDisplayName(TodoProjectSettingsWidget::tr("To-Do Settings"));
panelFactory->setCreateWidgetFunction([this, panelFactory](ProjectExplorer::Project *project) -> QWidget * {
auto *panel = new ProjectExplorer::PropertiesPanel;
panel->setDisplayName(panelFactory->displayName());
auto *widget = new TodoProjectSettingsWidget(project);
connect(widget, &TodoProjectSettingsWidget::projectSettingsChanged,
m_todoItemsProvider, [this, project](){m_todoItemsProvider->projectSettingsChanged(project);});
panel->setWidget(widget);
auto *panelsWidget = new ProjectExplorer::PanelsWidget();
panelsWidget->addPropertiesPanel(panel);
panelsWidget->setFocusProxy(widget);
return panelsWidget;
});
ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
return true;
}
......
/**************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "todoprojectsettingswidget.h"
#include "ui_todoprojectsettingswidget.h"
#include "constants.h"
#include <projectexplorer/project.h>
namespace Todo {
namespace Internal {
static const QString EXCLUDE_PLACEHOLDER = QObject::tr("<Enter regular expression to exclude>");
TodoProjectSettingsWidget::TodoProjectSettingsWidget(ProjectExplorer::Project *project) :
QWidget(0),
ui(new Ui::TodoProjectSettingsWidget),
m_project(project)
{
ui->setupUi(this);
setExcludedPatternsButtonsEnabled();
connect(ui->addExcludedPatternButton, &QPushButton::clicked,
this, &TodoProjectSettingsWidget::addExcludedPatternButtonClicked);
connect(ui->removeExcludedPatternButton, &QPushButton::clicked,
this, &TodoProjectSettingsWidget::removeExcludedPatternButtonClicked);
connect(ui->excludedPatternsList, &QListWidget::itemChanged,
this, &TodoProjectSettingsWidget::excludedPatternChanged, Qt::QueuedConnection);
loadSettings();
}
TodoProjectSettingsWidget::~TodoProjectSettingsWidget()
{
delete ui;
}
QListWidgetItem *TodoProjectSettingsWidget::addToExcludedPatternsList(const QString &pattern)
{