Commit ae505e22 authored by Waqar Ahmed's avatar Waqar Ahmed Committed by Christoph Cullmann
Browse files

Disable word-diff and just use normal diff for now

- Word diff doesn't show space change/new line change :/
- Split the classes into files
- Line numbers are available now
parent 7de7ca01
......@@ -630,7 +630,7 @@ void GitWidget::showDiff(const QString &file, bool staged, bool showInKate)
args.append(QStringLiteral("--staged"));
}
if (showInKate) {
args.append(QStringLiteral("--word-diff=porcelain"));
// args.append(QStringLiteral("--word-diff=porcelain"));
}
if (!file.isEmpty()) {
......
......@@ -112,6 +112,8 @@ target_sources(
hostprocess.cpp
diffwidget.cpp
diffeditor.cpp
difflinenumarea.cpp
)
if(BUILD_TESTING)
......
#include "diffeditor.h"
#include "difflinenumarea.h"
#include "ktexteditor_utils.h"
#include <QPainter>
#include <QPainterPath>
#include <QTextBlock>
DiffEditor::DiffEditor(QWidget *parent)
: QPlainTextEdit(parent)
, m_lineNumArea(new LineNumArea(this))
{
setFont(Utils::editorFont());
red1 = QColor("#c87872");
red1.setAlphaF(0.2);
green1 = QColor("#678528");
green1.setAlphaF(0.2);
auto c = QColor(254, 147, 140);
c.setAlphaF(0.1);
red2 = c;
c = QColor(166, 226, 46);
c.setAlphaF(0.1);
green2 = c;
setFrameStyle(QFrame::NoFrame);
auto updateEditorColors = [this](KTextEditor::Editor *e) {
if (!e)
return;
auto theme = e->theme();
auto bg = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::BackgroundColor));
auto fg = QColor::fromRgba(theme.textColor(KSyntaxHighlighting::Theme::TextStyle::Normal));
auto sel = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::TextSelection));
auto pal = palette();
pal.setColor(QPalette::Base, bg);
pal.setColor(QPalette::Text, fg);
pal.setColor(QPalette::Highlight, sel);
pal.setColor(QPalette::HighlightedText, fg);
setPalette(pal);
};
connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, updateEditorColors);
updateEditorColors(KTextEditor::Editor::instance());
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, [this] {
m_lineNumArea->update();
});
connect(this, &QPlainTextEdit::cursorPositionChanged, this, [this] {
m_lineNumArea->update();
});
connect(document(), &QTextDocument::blockCountChanged, this, &DiffEditor::updateLineNumberAreaWidth);
connect(this, &QPlainTextEdit::updateRequest, this, &DiffEditor::updateLineNumberArea);
}
void DiffEditor::resizeEvent(QResizeEvent *event)
{
QPlainTextEdit::resizeEvent(event);
updateLineNumAreaGeometry();
}
void DiffEditor::updateLineNumberArea(const QRect &rect, int dy)
{
if (dy)
m_lineNumArea->scroll(0, dy);
else
m_lineNumArea->update(0, rect.y(), m_lineNumArea->sizeHint().width(), rect.height());
updateLineNumAreaGeometry();
if (rect.contains(viewport()->rect())) {
updateLineNumberAreaWidth(0);
}
}
void DiffEditor::updateLineNumAreaGeometry()
{
const auto contentsRect = this->contentsRect();
const QRect newGeometry = {contentsRect.left(), contentsRect.top(), m_lineNumArea->sizeHint().width(), contentsRect.height()};
auto oldGeometry = m_lineNumArea->geometry();
if (newGeometry != oldGeometry) {
m_lineNumArea->setGeometry(newGeometry);
}
}
void DiffEditor::updateLineNumberAreaWidth(int)
{
QSignalBlocker blocker(this);
const auto oldMargins = viewportMargins();
const int width = m_lineNumArea->sizeHint().width();
const auto newMargins = QMargins{width, oldMargins.top(), oldMargins.right(), oldMargins.bottom()};
if (newMargins != oldMargins) {
setViewportMargins(newMargins);
}
}
void DiffEditor::paintEvent(QPaintEvent *e)
{
bool textPainted = false;
if (!getPaintContext().selections.isEmpty()) {
QPlainTextEdit::paintEvent(e);
textPainted = true;
}
QPainter p(viewport());
QPointF offset(contentOffset());
QTextBlock block = firstVisibleBlock();
const auto viewportRect = viewport()->rect();
while (block.isValid()) {
QRectF r = blockBoundingRect(block).translated(offset);
auto layout = block.layout();
auto hl = dataForLine(block.blockNumber());
if (hl && layout) {
const auto changes = hl->changes;
for (auto c : changes) {
// full line background is colored
p.fillRect(r, hl->added ? green1 : red1);
if (c.len >= block.text().length()) {
continue;
}
// qDebug() << "..." << c.len << block.text().length();
QTextLine sl = layout->lineForTextPosition(c.pos);
QTextLine el = layout->lineForTextPosition(c.pos + c.len);
// color any word diffs
if (sl.isValid() && sl.lineNumber() == el.lineNumber()) {
int sx = sl.cursorToX(c.pos);
int ex = el.cursorToX(c.pos + c.len);
QRectF r = sl.naturalTextRect();
r.setLeft(sx);
r.setRight(ex);
r.moveTop(offset.y() + (sl.height() * sl.lineNumber()));
p.fillRect(r, hl->added ? green2 : red2);
} else {
QPainterPath path;
int i = sl.lineNumber() + 1;
int end = el.lineNumber();
QRectF rect = sl.naturalTextRect();
rect.setLeft(sl.cursorToX(c.pos));
rect.moveTop(offset.y() + (sl.height() * sl.lineNumber()));
path.addRect(rect);
for (; i <= end; ++i) {
auto line = layout->lineAt(i);
rect = line.naturalTextRect();
rect.moveTop(offset.y() + (line.height() * line.lineNumber()));
if (i == end) {
rect.setRight(el.cursorToX(c.pos + c.len));
}
path.addRect(rect);
}
p.fillPath(path, hl->added ? green2 : red2);
}
}
}
if (block.text().startsWith(QStringLiteral("@@ "))) {
p.save();
p.setPen(Qt::red);
p.setBrush(Qt::NoBrush);
QRectF copy = r;
copy.setRight(copy.right() - 1);
p.drawRect(copy);
p.restore();
}
offset.ry() += r.height();
if (offset.y() > viewportRect.height()) {
break;
}
block = block.next();
}
if (!textPainted) {
QPlainTextEdit::paintEvent(e);
}
}
const LineHighlight *DiffEditor::dataForLine(int line)
{
auto it = std::find_if(m_data.cbegin(), m_data.cend(), [line](LineHighlight hl) {
return hl.line == line;
});
return it == m_data.cend() ? nullptr : &(*it);
}
void DiffEditor::setLineNumberData(QVector<int> data, int maxLineNum)
{
m_lineNumArea->setLineNumData(std::move(data));
m_lineNumArea->setMaxLineNum(maxLineNum);
updateLineNumberAreaWidth(0);
}
#ifndef KATE_DIFF_EDITOR
#define KATE_DIFF_EDITOR
#include <QPlainTextEdit>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
using IntT = qsizetype;
#else
using IntT = int;
#endif
struct Change {
IntT pos;
IntT len;
};
struct LineHighlight {
QVector<Change> changes;
IntT line;
bool added;
};
class DiffEditor : public QPlainTextEdit
{
Q_OBJECT
friend class LineNumArea;
public:
DiffEditor(QWidget *parent = nullptr);
void clearData()
{
m_data.clear();
}
void appendData(const QVector<LineHighlight> &newData)
{
m_data.append(newData);
}
void setLineNumberData(QVector<int> data, int maxLineNum);
protected:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *e) override;
private:
const LineHighlight *dataForLine(int line);
void updateLineNumberArea(const QRect &rect, int dy);
void updateLineNumAreaGeometry();
void updateLineNumberAreaWidth(int);
QVector<LineHighlight> m_data;
QColor red1;
QColor red2;
QColor green1;
QColor green2;
class LineNumArea *const m_lineNumArea;
};
#endif
#include "difflinenumarea.h"
#include "diffeditor.h"
#include <QPainter>
#include <QTextBlock>
#include <KTextEditor/Editor>
LineNumArea::LineNumArea(DiffEditor *parent)
: QWidget(parent)
, textEdit(parent)
{
setFont(textEdit->font());
auto updateColors = [this](KTextEditor::Editor *e) {
if (!e)
return;
auto theme = e->theme();
m_currentLineColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::CurrentLineNumber));
m_otherLinesColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::LineNumbers));
m_borderColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::Separator));
auto bg = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::IconBorder));
auto pal = palette();
pal.setColor(QPalette::Window, bg);
setPalette(pal);
};
connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, updateColors);
updateColors(KTextEditor::Editor::instance());
}
int LineNumArea::lineNumAreaWidth() const
{
int digits = 2;
int max = std::max(1, maxLineNum);
while (max >= 10) {
max /= 10;
++digits;
}
return 13 + textEdit->fontMetrics().horizontalAdvance(u'9') * digits;
}
void LineNumArea::setLineNumData(QVector<int> data)
{
m_lineToNum = std::move(data);
}
QSize LineNumArea::sizeHint() const
{
return {lineNumAreaWidth(), 0};
}
void LineNumArea::paintEvent(QPaintEvent *event)
{
if (m_lineToNum.isEmpty()) {
return;
}
QPainter painter(this);
painter.fillRect(event->rect(), palette().color(QPalette::Active, QPalette::Window));
auto block = textEdit->firstVisibleBlock();
int blockNumber = block.blockNumber();
qreal top = textEdit->blockBoundingGeometry(block).translated(textEdit->contentOffset()).top();
// Maybe the top is not 0?
top += textEdit->viewportMargins().top();
qreal bottom = top;
const QPen currentLine = m_currentLineColor;
const QPen otherLines = m_otherLinesColor;
painter.setFont(font());
while (block.isValid() && top <= event->rect().bottom()) {
top = bottom;
bottom = top + textEdit->blockBoundingRect(block).height();
if (block.isVisible() && bottom >= event->rect().top()) {
int n = m_lineToNum.value(blockNumber, -1);
if (n > -1) {
const QString number = QString::number(n);
auto isCurrentLine = textEdit->textCursor().blockNumber() == blockNumber;
painter.setPen(isCurrentLine ? currentLine : otherLines);
painter.drawText(-5, top, sizeHint().width(), textEdit->fontMetrics().height(), Qt::AlignRight, number);
}
}
block = block.next();
++blockNumber;
}
painter.setPen(m_borderColor);
painter.drawLine(rect().topRight() - QPoint(1, 0), rect().bottomRight() - QPoint(1, 0));
}
#ifndef DIFF_LINE_NUM_AREA
#define DIFF_LINE_NUM_AREA
#include <QWidget>
class LineNumArea final : public QWidget
{
Q_OBJECT
public:
explicit LineNumArea(class DiffEditor *parent);
int lineNumAreaWidth() const;
QSize sizeHint() const override;
void setLineNumData(QVector<int>);
void setMaxLineNum(int n)
{
maxLineNum = n;
}
protected:
void paintEvent(QPaintEvent *event) override;
private:
class DiffEditor *const textEdit;
QColor m_currentLineColor;
QColor m_otherLinesColor;
QColor m_borderColor;
QVector<int> m_lineToNum;
int maxLineNum = 0;
};
#endif
......@@ -3,8 +3,11 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "diffwidget.h"
#include "gitprocess.h"
#include "diffeditor.h"
#include "ktexteditor_utils.h"
#include "gitprocess.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QPainter>
......@@ -18,199 +21,18 @@
#include <KSyntaxHighlighting/Repository>
#include <KSyntaxHighlighting/SyntaxHighlighter>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
using IntT = qsizetype;
std::pair<uint, uint> parseRange(const QString &range)
{
int commaPos = range.indexOf(QLatin1Char(','));
if (commaPos > -1) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
return {range.midRef(0, commaPos).toInt(), range.midRef(commaPos + 1).toInt()};
#else
using IntT = int;
return {QStringView(range).sliced(0, commaPos).toInt(), QStringView(range).sliced(commaPos + 1).toInt()};
#endif
struct Change {
IntT pos;
IntT len;
};
struct LineHilight {
QVector<Change> changes;
IntT line;
bool added;
};
class DiffHighlighter : public QSyntaxHighlighter
{
public:
DiffHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
}
void highlightBlock(const QString &) override
{
auto block = currentBlock();
int num = block.blockNumber();
auto it = std::find_if(data.cbegin(), data.cend(), [num](LineHilight hl) {
return hl.line == num;
});
if (it != data.cend()) {
QColor color = it->added ? Qt::green : Qt::red;
const auto changes = it->changes;
for (const auto c : changes) {
setFormat(c.pos, c.len, color);
}
}
}
void clearData()
{
data.clear();
}
void appendData(const QVector<LineHilight> &newData)
{
data.append(newData);
}
private:
QVector<LineHilight> data;
};
class DiffEditor : public QPlainTextEdit
{
public:
DiffEditor(QWidget *parent = nullptr)
: QPlainTextEdit(parent)
{
red1 = QColor("#c87872");
red1.setAlphaF(0.2);
green1 = QColor("#678528");
green1.setAlphaF(0.2);
auto c = QColor(254, 147, 140);
c.setAlphaF(0.1);
red2 = c;
c = QColor(166, 226, 46);
c.setAlphaF(0.1);
green2 = c;
auto updateEditorColors = [this](KTextEditor::Editor *e) {
if (!e)
return;
auto theme = e->theme();
auto bg = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::BackgroundColor));
auto fg = QColor::fromRgba(theme.textColor(KSyntaxHighlighting::Theme::TextStyle::Normal));
auto sel = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::TextSelection));
auto pal = palette();
pal.setColor(QPalette::Base, bg);
pal.setColor(QPalette::Text, fg);
pal.setColor(QPalette::Highlight, sel);
pal.setColor(QPalette::HighlightedText, fg);
setPalette(pal);
};
connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, updateEditorColors);
updateEditorColors(KTextEditor::Editor::instance());
}
void paintEvent(QPaintEvent *e) override
{
bool textPainted = false;
if (!getPaintContext().selections.isEmpty()) {
QPlainTextEdit::paintEvent(e);
textPainted = true;
}
QPainter p(viewport());
QPointF offset(contentOffset());
QTextBlock block = firstVisibleBlock();
const auto viewportRect = viewport()->rect();
while (block.isValid()) {
QRectF r = blockBoundingRect(block).translated(offset);
auto layout = block.layout();
auto hl = dataForLine(block.blockNumber());
if (hl && layout) {
const auto changes = hl->changes;
for (auto c : changes) {
// full line background is colored
p.fillRect(r, hl->added ? green1 : red1);
QTextLine sl = layout->lineForTextPosition(c.pos);
QTextLine el = layout->lineForTextPosition(c.pos + c.len);
// color any word diffs
if (sl.isValid() && sl.lineNumber() == el.lineNumber()) {
int sx = sl.cursorToX(c.pos);
int ex = el.cursorToX(c.pos + c.len);
QRectF r = sl.naturalTextRect();
r.setLeft(sx);
r.setRight(ex);
r.moveTop(offset.y() + (sl.height() * sl.lineNumber()));
p.fillRect(r, hl->added ? green2 : red2);
} else {
QPainterPath path;
int i = sl.lineNumber() + 1;
int end = el.lineNumber();
QRectF rect = sl.naturalTextRect();
rect.setLeft(sl.cursorToX(c.pos));
rect.moveTop(offset.y() + (sl.height() * sl.lineNumber()));
path.addRect(rect);
for (; i <= end; ++i) {
auto line = layout->lineAt(i);
rect = line.naturalTextRect();
rect.moveTop(offset.y() + (line.height() * line.lineNumber()));
if (i == end) {
rect.setRight(el.cursorToX(c.pos + c.len));
}
path.addRect(rect);
}
p.fillPath(path, hl->added ? green2 : red2);
}
}
}
if (block.text().startsWith(QStringLiteral("@@ "))) {
p.save();
p.setPen(Qt::red);
p.setBrush(Qt::NoBrush);
QRectF copy = r;
copy.setRight(copy.right() - 1);
p.drawRect(copy);
p.restore();
}
offset.ry() += r.height();
if (offset.y() > viewportRect.height()) {
break;
}
block = block.next();
}
if (!textPainted) {
QPlainTextEdit::paintEvent(e);
}
}
void clearData()
{
data.clear();
}
void appendData(const QVector<LineHilight> &newData)
{
data.append(newData);
}
const LineHilight *dataForLine(int line)
{
auto it = std::find_if(data.cbegin(), data.cend(), [line](LineHilight hl) {
</