308 lines
8.1 KiB
C++
308 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "todoparser.h"
|
|
#include "mainwindow.h"
|
|
#include "editor.h"
|
|
#include "editorlist.h"
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
static QRegularExpression todoReg("\\b(todo|fixme)\\b", QRegularExpression::CaseInsensitiveOption);
|
|
TodoParser::TodoParser(QObject *parent) : QObject(parent),
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
mMutex()
|
|
#else
|
|
mMutex(QMutex::Recursive)
|
|
#endif
|
|
{
|
|
mThread = nullptr;
|
|
}
|
|
|
|
void TodoParser::parseFile(const QString &filename,bool isForProject)
|
|
{
|
|
QMutexLocker locker(&mMutex);
|
|
if (mThread) {
|
|
return;
|
|
}
|
|
mThread = new TodoThread(filename);
|
|
connect(mThread,&QThread::finished,
|
|
[this] {
|
|
QMutexLocker locker(&mMutex);
|
|
if (mThread) {
|
|
mThread->deleteLater();
|
|
mThread = nullptr;
|
|
}
|
|
});
|
|
if (!isForProject) {
|
|
connect(mThread, &TodoThread::parseStarted,
|
|
pMainWindow, &MainWindow::onTodoParseStarted);
|
|
}
|
|
connect(mThread, &TodoThread::parsingFile,
|
|
pMainWindow, &MainWindow::onTodoParsingFile);
|
|
connect(mThread, &TodoThread::todoFound,
|
|
pMainWindow, &MainWindow::onTodoFound);
|
|
connect(mThread, &TodoThread::parseFinished,
|
|
pMainWindow, &MainWindow::onTodoParseFinished);
|
|
mThread->start();
|
|
}
|
|
|
|
void TodoParser::parseFiles(const QStringList &files)
|
|
{
|
|
QMutexLocker locker(&mMutex);
|
|
if (mThread) {
|
|
return;
|
|
}
|
|
mThread = new TodoThread(files);
|
|
connect(mThread,&QThread::finished,
|
|
[this] {
|
|
QMutexLocker locker(&mMutex);
|
|
if (mThread) {
|
|
mThread->deleteLater();
|
|
mThread = nullptr;
|
|
}
|
|
});
|
|
connect(mThread, &TodoThread::parseStarted,
|
|
pMainWindow, &MainWindow::onTodoParseStarted);
|
|
connect(mThread, &TodoThread::parsingFile,
|
|
pMainWindow, &MainWindow::onTodoParsingFile);
|
|
connect(mThread, &TodoThread::todoFound,
|
|
pMainWindow, &MainWindow::onTodoFound);
|
|
connect(mThread, &TodoThread::parseFinished,
|
|
pMainWindow, &MainWindow::onTodoParseFinished);
|
|
mThread->start();
|
|
}
|
|
|
|
bool TodoParser::parsing() const
|
|
{
|
|
return (mThread!=nullptr);
|
|
}
|
|
|
|
TodoThread::TodoThread(const QString &filename, QObject *parent): QThread(parent)
|
|
{
|
|
mFilename = filename;
|
|
mParseFiles = false;
|
|
}
|
|
|
|
TodoThread::TodoThread(const QStringList &files, QObject *parent): QThread(parent)
|
|
{
|
|
mFiles = files;
|
|
mParseFiles = true;
|
|
}
|
|
|
|
void TodoThread::parseFile()
|
|
{
|
|
QSynedit::PHighlighter highlighter = highlighterManager.getCppHighlighter();
|
|
emit parseStarted();
|
|
doParseFile(mFilename,highlighter);
|
|
emit parseFinished();
|
|
}
|
|
|
|
void TodoThread::parseFiles()
|
|
{
|
|
QSynedit::PHighlighter highlighter = highlighterManager.getCppHighlighter();
|
|
emit parseStarted();
|
|
foreach(const QString& filename,mFiles) {
|
|
doParseFile(filename,highlighter);
|
|
}
|
|
emit parseFinished();
|
|
}
|
|
|
|
void TodoThread::doParseFile(const QString &filename, QSynedit::PHighlighter highlighter)
|
|
{
|
|
emit parsingFile(filename);
|
|
QStringList lines;
|
|
if (!pMainWindow->editorList()->getContentFromOpenedEditor(filename,lines)) {
|
|
lines = readFileToLines(filename);
|
|
}
|
|
QSynedit::PHighlighterAttribute commentAttr = highlighter->getAttribute(SYNS_AttrComment);
|
|
|
|
highlighter->resetState();
|
|
for (int i =0;i<lines.count();i++) {
|
|
highlighter->setLine(lines[i],i);
|
|
while (!highlighter->eol()) {
|
|
QSynedit::PHighlighterAttribute attr;
|
|
attr = highlighter->getTokenAttribute();
|
|
if (attr == commentAttr) {
|
|
QString token = highlighter->getToken();
|
|
int pos = token.indexOf(todoReg);
|
|
if (pos>=0) {
|
|
emit todoFound(
|
|
filename,
|
|
i+1,
|
|
pos+highlighter->getTokenPos(),
|
|
lines[i].trimmed()
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
highlighter->next();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void TodoThread::run()
|
|
{
|
|
if (mParseFiles) {
|
|
parseFiles();
|
|
} else {
|
|
parseFile();
|
|
}
|
|
}
|
|
|
|
TodoModel::TodoModel(QObject *parent) : QAbstractListModel(parent)
|
|
{
|
|
mIsForProject=false;
|
|
}
|
|
|
|
void TodoModel::addItem(const QString &filename, int lineNo, int ch, const QString &line)
|
|
{
|
|
QList<PTodoItem> &items=getItems(mIsForProject);
|
|
int pos=-1;
|
|
for (int i=0;i<items.count();i++) {
|
|
int comp=QString::compare(filename,items[i]->filename);
|
|
if (comp<0) {
|
|
pos=i;
|
|
break;
|
|
} else if (comp==0) {
|
|
if (lineNo<items[i]->lineNo) {
|
|
pos=i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pos<0) {
|
|
pos=items.count();
|
|
}
|
|
beginInsertRows(QModelIndex(),pos,pos);
|
|
PTodoItem item = std::make_shared<TodoItem>();
|
|
item->filename = filename;
|
|
item->lineNo = lineNo;
|
|
item->ch = ch;
|
|
item->line = line;
|
|
items.insert(pos,item);
|
|
endInsertRows();
|
|
}
|
|
|
|
void TodoModel::removeTodosForFile(const QString &filename)
|
|
{
|
|
QList<PTodoItem> &items=getItems(mIsForProject);
|
|
for(int i=items.count()-1;i>=0;i--) {
|
|
PTodoItem item = items[i];
|
|
if (item->filename==filename) {
|
|
beginRemoveRows(QModelIndex(),i,i);
|
|
items.removeAt(i);
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TodoModel::clear()
|
|
{
|
|
beginResetModel();
|
|
QList<PTodoItem> &items=getItems(mIsForProject);
|
|
items.clear();
|
|
endResetModel();
|
|
}
|
|
|
|
void TodoModel::clear(bool forProject)
|
|
{
|
|
if (mIsForProject == forProject)
|
|
beginResetModel();
|
|
QList<PTodoItem> &items=getItems(forProject);
|
|
items.clear();
|
|
if (mIsForProject == forProject)
|
|
endResetModel();
|
|
}
|
|
|
|
PTodoItem TodoModel::getItem(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid())
|
|
return PTodoItem();
|
|
return getItems(mIsForProject)[index.row()];
|
|
}
|
|
|
|
QList<PTodoItem> &TodoModel::getItems(bool forProject)
|
|
{
|
|
return forProject?mProjectItems:mItems;
|
|
}
|
|
|
|
const QList<PTodoItem> &TodoModel::getConstItems(bool forProject) const
|
|
{
|
|
return forProject?mProjectItems:mItems;
|
|
}
|
|
|
|
bool TodoModel::isForProject() const
|
|
{
|
|
return mIsForProject;
|
|
}
|
|
|
|
void TodoModel::setIsForProject(bool newIsForProject)
|
|
{
|
|
if (mIsForProject!=newIsForProject) {
|
|
beginResetModel();
|
|
mIsForProject = newIsForProject;
|
|
endResetModel();
|
|
}
|
|
}
|
|
|
|
int TodoModel::rowCount(const QModelIndex &) const
|
|
{
|
|
const QList<PTodoItem> &items=getConstItems(mIsForProject);
|
|
return items.count();
|
|
}
|
|
|
|
QVariant TodoModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
const QList<PTodoItem> &items=getConstItems(mIsForProject);
|
|
if (!index.isValid())
|
|
return QVariant();
|
|
if (role==Qt::DisplayRole) {
|
|
PTodoItem item = items[index.row()];
|
|
switch(index.column()) {
|
|
case 0:
|
|
return item->filename;
|
|
case 1:
|
|
return item->lineNo;
|
|
case 2:
|
|
return item->line;
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant TodoModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
|
switch(section) {
|
|
case 0:
|
|
return tr("Filename");
|
|
case 1:
|
|
return tr("Line");
|
|
case 2:
|
|
return tr("Content");
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
int TodoModel::columnCount(const QModelIndex &) const
|
|
{
|
|
return 3;
|
|
}
|