/* * * TODO plugin - Add pane with list all TODO, FIXME, etc. comments. * * Copyright (C) 2010 VasiliySorokin * * Authors: Vasiliy Sorokin * * This file is part of TODO plugin for QtCreator. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * * Neither the name of the vsorokin nor the names of its contributors may be used to endorse or * promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "todoplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TodoPlugin::TodoPlugin() { qRegisterMetaTypeStreamOperators("Keyword"); qRegisterMetaTypeStreamOperators("KeywordsList"); currentProject = 0; inReading = false; readSettings(); } TodoPlugin::~TodoPlugin() { // Do notning } void TodoPlugin::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("TODOPlugin"); projectOptions = settings->value("project_options", 0).toInt(); paneOptions = settings->value("pane_options", 0).toInt(); KeywordsList defaultKeywords; defaultKeywords.append(Keyword("TODO", QIcon(":/warning"), QColor("#BFFFC8"))); defaultKeywords.append(Keyword("NOTE", QIcon(":/info"), QColor("#E2DFFF"))); defaultKeywords.append(Keyword("FIXME", QIcon(":/error"), QColor("#FFBFBF"))); defaultKeywords.append(Keyword("BUG", QIcon(":/error"), QColor("#FFDFDF"))); defaultKeywords.append(Keyword("HACK", QIcon(":/info"), QColor("#FFFFAA"))); keywords = settings->value("keywords", qVariantFromValue(defaultKeywords)).value(); settings->endGroup(); } bool TodoPlugin::initialize(const QStringList& args, QString *errMsg) { Q_UNUSED(args); Q_UNUSED(errMsg); patternString = generatePatternString(); settingsPage = new SettingsPage(keywords, projectOptions, paneOptions, this); if (paneOptions == 0) { outPane = new TodoOutputPane(this); addAutoReleasedObject(outPane); connect(outPane->getTodoList(), SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); connect(outPane->getTodoList(), SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); } else { ExtensionSystem::PluginManager* pluginManager = ExtensionSystem::PluginManager::instance(); taskHub = pluginManager->getObject(); if (!taskHub) { paneOptions = 1; outPane = new TodoOutputPane(this); addAutoReleasedObject(outPane); connect(outPane->getTodoList(), SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); connect(outPane->getTodoList(), SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); QMessageBox::warning((QWidget *)Core::ICore::instance()->mainWindow(), tr("TODO plugin"), tr("Task window object not found.\nWork in TODO Output pane.")); } else { taskHub->addCategory("todoplugin", tr("TODOs comments")); } } addAutoReleasedObject(settingsPage); connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), this, SLOT(currentEditorChanged(Core::IEditor*))); if (projectOptions != 0) { connect(ProjectExplorer::ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), this, SLOT(projectChanged(ProjectExplorer::Project *))); } return true; } void TodoPlugin::extensionsInitialized() { // Do nothing } void TodoPlugin::shutdown() { // Do nothing } void TodoPlugin::showPane() { if (paneOptions == 0) { outPane->visibilityChanged(true); outPane->setFocus(); } } void TodoPlugin::gotoToRowInFile(QListWidgetItem *item) { int row = item->data(Qt::UserRole + 2).toInt(); QString file = item->data(Qt::UserRole + 1).toString(); if (QFileInfo(file).exists()) { Core::IEditor *editor = Core::EditorManager::instance()->openEditor(file); editor->gotoLine(row); } } void TodoPlugin::currentEditorChanged(Core::IEditor *editor) { if (inReading) return; if (projectOptions == 0) { if (paneOptions == 0) { outPane->clearContents(); } else { taskHub->clearTasks("todoplugin"); } } if (!editor) { return; } connect(editor->file(), SIGNAL(changed()), this, SLOT(fileChanged())); QString fileName = editor->file()->fileName(); if (projectOptions == 0) readFile(fileName); } void TodoPlugin::removeFromLocalTasks(QString filename) { for (int i = 0; i < tasks.count(); ++i) { if (!tasks.at(i).file.compare(filename)) { tasks.removeAt(i); } } } void TodoPlugin::addLocalTaskToTaskWindow() { for (int i = 0; i < tasks.count(); ++i) { taskHub->addTask(tasks.at(i)); } } void TodoPlugin::fileChanged() { Core::IFile *file = (Core::IFile *)sender(); if (file) { if (projectOptions == 0) { if (paneOptions == 0) { outPane->clearContents(); } else { taskHub->clearTasks("todoplugin"); } } else { if (paneOptions == 0) { outPane->clearContents(file->fileName()); } else { taskHub->clearTasks("todoplugin"); removeFromLocalTasks(file->fileName()); } } readFile(file->fileName()); } } Keyword TodoPlugin::prepareOutputString(QString &text) { Keyword keyword; for(int i = 0; i < keywords.count(); ++i) { QRegExp keywordExp("//\\s*" + keywords.at(i).name + "(:|\\s)", Qt::CaseInsensitive); if (text.contains(keywordExp)) { text = text.replace("\n", ""); text = text.replace("\r", ""); text = text.replace(keywordExp, keywords.at(i).name + ": "); text = text.trimmed(); keyword = keywords.at(i); break; } } return keyword; } void TodoPlugin::readFile(QString fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) return; int i = 1; while (!file.atEnd()) { QString currentLine = file.readLine(); if (currentLine.contains(QRegExp(patternString, Qt::CaseInsensitive))) { Keyword resultKeyword = prepareOutputString(currentLine); QTextCodec *unicodeCodec = QTextCodec::codecForLocale(); currentLine = unicodeCodec->toUnicode(currentLine.toAscii()); if (paneOptions == 0) { outPane->addItem(currentLine, fileName, i, resultKeyword.icon, resultKeyword.warningColor); if (!inReading) outPane->sort(); } else { ProjectExplorer::Task task(ProjectExplorer::Task::Unknown, currentLine, fileName, i, "todoplugin"); tasks.append(task); } } ++i; } if (paneOptions != 0 && !inReading) { qSort(tasks.begin(), tasks.end(), TodoPlugin::taskLessThan); addLocalTaskToTaskWindow(); } } QString TodoPlugin::generatePatternString() { QString result = ""; if (keywords.count()) { for (int i = 0; i < keywords.count() - 1; ++i) { result += "//\\s*" + keywords.at(i).name + "(:|\\s)|"; } result += "//\\s*" + keywords.at(keywords.count() - 1).name + "(:|\\s)"; } return result; } void TodoPlugin::projectChanged(ProjectExplorer::Project *project) { if (!project) return; if (inReading) return; currentProject = project; if (paneOptions == 0) { outPane->clearContents(); } else { taskHub->clearTasks("todoplugin"); } inReading = true; QFuture result = QtConcurrent::run(&TodoPlugin::readCurrentProject, this); Core::ICore::instance()->progressManager()->addTask(result, tr("Todoscan"), "Todo.Plugin.Scanning"); } void TodoPlugin::readCurrentProject(QFutureInterface &future, TodoPlugin *instance) { QStringList filesList = instance->currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles); future.setProgressRange(0, filesList.count()-1); for (int i = 0; i < filesList.count(); ++i) { instance->readFile(filesList.at(i)); future.setProgressValue(i); } if (instance->paneOptions == 0) { instance->outPane->sort(); } else { qSort(instance->tasks.begin(), instance->tasks.end(), TodoPlugin::taskLessThan); instance->addLocalTaskToTaskWindow(); } instance->inReading = false; future.reportFinished(); } bool TodoPlugin::taskLessThan(const ProjectExplorer::Task &t1, const ProjectExplorer::Task &t2) { if (!t1.file.right(t1.file.size() - t1.file.lastIndexOf("/") - 1).compare(t2.file.right(t2.file.size() - t2.file.lastIndexOf("/") - 1))) { if (t1.line < t2.line) { return true; } else { return false; } } else { return t1.file.right(t1.file.size() - t1.file.lastIndexOf("/") - 1).compare(t2.file.right(t2.file.size() - t2.file.lastIndexOf("/") - 1)) < 0; } } Q_EXPORT_PLUGIN(TodoPlugin)