Commit 54865897 authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Show lines add/remove in diff

parent 92a224f1
......@@ -2,32 +2,131 @@
#include "kateprojectpluginview.h"
#include "kateprojectworker.h"
#include <QPainter>
#include <QProcess>
#include <QStyledItemDelegate>
#include <QVBoxLayout>
#include <KLocalizedString>
#include <QProcess>
static QVariantMap createMap(const QStringList &files)
#include <QDebug>
class DiffStyleDelegate : public QStyledItemDelegate
{
QVariantMap cnf, filesMap;
filesMap[QStringLiteral("list")] = (files);
cnf[QStringLiteral("name")] = QStringLiteral("ComparingBranches");
cnf[QStringLiteral("files")] = (QVariantList() << filesMap);
return cnf;
public:
DiffStyleDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
if (index.data(KateProjectItem::TypeRole).toInt() == KateProjectItem::Directory) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
painter->save();
// paint background
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
} else {
painter->fillRect(option.rect, option.palette.base());
}
int add = index.data(Qt::UserRole + 2).toInt();
int sub = index.data(Qt::UserRole + 3).toInt();
QString adds = QString(QStringLiteral("+") + QString::number(add));
QString subs = QString(QStringLiteral(" -") + QString::number(sub));
QString file = options.text;
options.text = QString(); // clear old text
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
QRect r = options.rect;
// don't draw over icon
r.setX(r.x() + option.decorationSize.width() + 5);
auto &fm = options.fontMetrics;
// adds width
int aw = fm.horizontalAdvance(adds);
// subs width
int sw = fm.horizontalAdvance(subs);
// subtract this from total width of rect
int totalw = r.width();
totalw = totalw - (aw + sw);
// get file name, elide if necessary
QString filename = fm.elidedText(file, Qt::ElideRight, totalw);
painter->drawText(r, Qt::AlignVCenter, filename);
static constexpr auto red = QColor(237, 21, 21); // Breeze Danger Red
static constexpr auto green = QColor(17, 209, 27); // Breeze Verdant Green
r.setX(r.x() + totalw);
painter->setPen(green);
painter->drawText(r, Qt::AlignVCenter, adds);
painter->setPen(red);
r.setX(r.x() + aw);
painter->drawText(r, Qt::AlignVCenter, subs);
painter->restore();
}
};
static void createFileTree(QStandardItem *parent, const QString &basePath, const QVector<GitUtils::StatusItem> &files)
{
QDir dir(basePath);
const QString dirPath = dir.path() + QLatin1Char('/');
QHash<QString, QStandardItem *> dir2Item;
dir2Item[QString()] = parent;
for (const auto &file : qAsConst(files)) {
const QString filePath = QString::fromUtf8(file.file);
/**
* cheap file name computation
* we do this A LOT, QFileInfo is very expensive just for this operation
*/
const int slashIndex = filePath.lastIndexOf(QLatin1Char('/'));
const QString fileName = (slashIndex < 0) ? filePath : filePath.mid(slashIndex + 1);
const QString filePathName = (slashIndex < 0) ? QString() : filePath.left(slashIndex);
const QString fullFilePath = dirPath + filePath;
/**
* construct the item with right directory prefix
* already hang in directories in tree
*/
KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::File, fileName);
fileItem->setData(fullFilePath, Qt::UserRole);
fileItem->setData(file.statusChar, Qt::UserRole + 1);
fileItem->setData(file.linesAdded, Qt::UserRole + 2);
fileItem->setData(file.linesRemoved, Qt::UserRole + 3);
// put in our item to the right directory parent
KateProjectWorker::directoryParent(dir, dir2Item, filePathName)->appendRow(fileItem);
}
}
CompareBranchesView::CompareBranchesView(QWidget *parent, const QString &gitPath, const QString fromB, const QString &toBr, QStringList files)
CompareBranchesView::CompareBranchesView(QWidget *parent, const QString &gitPath, const QString fromB, const QString &toBr, QVector<GitUtils::StatusItem> items)
: QWidget(parent)
, m_files(std::move(files))
, m_gitDir(gitPath)
, m_fromBr(fromB)
, m_toBr(toBr)
{
setLayout(new QVBoxLayout);
KateProjectWorker *w = new KateProjectWorker(m_gitDir, QString(), createMap(m_files), true);
connect(w, &KateProjectWorker::loadDone, this, &CompareBranchesView::loadFilesDone, Qt::QueuedConnection);
w->run();
QStandardItem *root = new QStandardItem;
createFileTree(root, m_gitDir, items);
m_model.clear();
m_model.invisibleRootItem()->appendColumn(root->takeColumn(0));
m_backBtn.setText(i18n("Back"));
m_backBtn.setIcon(QIcon::fromTheme(QStringLiteral("draw-arrow-back.svg")));
......@@ -38,17 +137,12 @@ CompareBranchesView::CompareBranchesView(QWidget *parent, const QString &gitPath
layout()->addWidget(&m_tree);
m_tree.setHeaderHidden(true);
m_tree.setItemDelegate(new DiffStyleDelegate(this));
m_tree.expandAll();
connect(&m_tree, &QTreeView::clicked, this, &CompareBranchesView::showDiff);
}
void CompareBranchesView::loadFilesDone(const KateProjectSharedQStandardItem &topLevel, KateProjectSharedQHashStringItem)
{
m_model.clear();
m_model.invisibleRootItem()->appendColumn(topLevel->takeColumn(0));
m_tree.expandAll();
}
void CompareBranchesView::showDiff(const QModelIndex &idx)
{
auto file = idx.data(Qt::UserRole).toString().remove(m_gitDir + QLatin1Char('/'));
......
......@@ -6,14 +6,14 @@
#include <QTreeView>
#include <QWidget>
#include <kateproject.h>
#include "git/gitstatus.h"
class KateProjectPluginView;
class CompareBranchesView : public QWidget
{
Q_OBJECT
public:
explicit CompareBranchesView(QWidget *parent, const QString &gitPath, const QString fromB, const QString &toBr, QStringList files);
explicit CompareBranchesView(QWidget *parent, const QString &gitPath, const QString fromB, const QString &toBr, QVector<GitUtils::StatusItem> items);
void setPluginView(KateProjectPluginView *pv)
{
m_pluginView = pv;
......@@ -22,13 +22,11 @@ public:
Q_SIGNAL void backClicked();
private Q_SLOTS:
void loadFilesDone(const KateProjectSharedQStandardItem &topLevel, KateProjectSharedQHashStringItem file2Item);
void showDiff(const QModelIndex &idx);
private:
QPushButton m_backBtn;
QTreeView m_tree;
QStringList m_files;
QStandardItemModel m_model;
QString m_gitDir;
QString m_fromBr;
......
......@@ -167,3 +167,23 @@ void GitUtils::parseDiffNumStat(QVector<GitUtils::StatusItem> &items, const QByt
addNumStat(items, add, sub, file);
}
}
QVector<GitUtils::StatusItem> GitUtils::parseDiffNameStatus(const QByteArray &raw)
{
const auto lines = raw.split('\n');
QVector<GitUtils::StatusItem> out;
out.reserve(lines.size());
for (const auto &l : lines) {
const auto cols = l.split('\t');
if (cols.size() < 2) {
continue;
}
GitUtils::StatusItem i;
i.statusChar = cols[0][0];
i.file = cols[1];
out.append(i);
}
return out;
}
......@@ -68,6 +68,8 @@ GitParsedStatus parseStatus(const QByteArray &raw);
void parseDiffNumStat(QVector<GitUtils::StatusItem> &items, const QByteArray &raw);
QVector<StatusItem> parseDiffNameStatus(const QByteArray &raw);
QString statusString(GitStatus s);
}
......
......@@ -738,7 +738,7 @@ void GitWidget::numStatForStatus(QVector<GitUtils::StatusItem> &list, bool modif
void GitWidget::branchCompareFiles(const QString &from, const QString &to)
{
// git diff br...br2 --name-only -z
const auto args = QStringList{QStringLiteral("diff"), QStringLiteral("%1...%2").arg(from).arg(to), QStringLiteral("--name-only"), QStringLiteral("-z")};
auto args = QStringList{QStringLiteral("diff"), QStringLiteral("%1...%2").arg(from).arg(to), QStringLiteral("--name-status")};
QProcess git;
git.setWorkingDirectory(m_gitPath);
......@@ -748,13 +748,27 @@ void GitWidget::branchCompareFiles(const QString &from, const QString &to)
return;
}
}
QList<QByteArray> files = git.readAllStandardOutput().split(0x00);
QStringList filesList;
std::transform(files.cbegin(), files.cend(), std::back_inserter(filesList), [](const QByteArray &a) {
return QString::fromUtf8(a);
});
CompareBranchesView *w = new CompareBranchesView(this, m_gitPath, from, to, filesList);
auto filesWithNameStatus = GitUtils::parseDiffNameStatus(git.readAllStandardOutput());
if (filesWithNameStatus.isEmpty()) {
sendMessage(i18n("Failed to compare %1...%2", from, to), true);
return;
}
// get --num-stat
args = QStringList{QStringLiteral("diff"), QStringLiteral("%1...%2").arg(from).arg(to), QStringLiteral("--numstat"), QStringLiteral("-z")};
git.setArguments(args);
git.start(QStringLiteral("git"), args, QProcess::ReadOnly);
if (git.waitForStarted() && git.waitForFinished(-1)) {
if (git.exitStatus() != QProcess::NormalExit || git.exitCode() != 0) {
sendMessage(i18n("Failed to get numstat when diffing %1...%2", from, to), true);
return;
}
}
GitUtils::parseDiffNumStat(filesWithNameStatus, git.readAllStandardOutput());
CompareBranchesView *w = new CompareBranchesView(this, m_gitPath, from, to, filesWithNameStatus);
w->setPluginView(m_pluginView);
connect(w, &CompareBranchesView::backClicked, this, [this] {
auto x = m_stackWid->currentWidget();
......
......@@ -130,7 +130,7 @@ void KateProjectWorker::loadProject(QStandardItem *parent, const QVariantMap &pr
* @param path current path we need item for
* @return correct parent item for given path, will reuse existing ones
*/
static QStandardItem *directoryParent(const QDir &base, QHash<QString, QStandardItem *> &dir2Item, QString path)
QStandardItem *KateProjectWorker::directoryParent(const QDir &base, QHash<QString, QStandardItem *> &dir2Item, QString path)
{
/**
* throw away simple /
......
......@@ -35,6 +35,8 @@ public:
void run() override;
static QStandardItem *directoryParent(const QDir &base, QHash<QString, QStandardItem *> &dir2Item, QString path);
Q_SIGNALS:
void loadDone(KateProjectSharedQStandardItem topLevel, KateProjectSharedQHashStringItem file2Item);
void loadIndexDone(KateProjectSharedProjectIndex index);
......
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