Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Utilities
Kate
Commits
2165e21e
Commit
2165e21e
authored
Sep 02, 2022
by
Waqar Ahmed
Committed by
Christoph Cullmann
Sep 03, 2022
Browse files
Add unified and raw diff styles
parent
cdbf4e11
Changes
4
Hide whitespace changes
Inline
Side-by-side
apps/lib/diffeditor.cpp
View file @
2165e21e
...
...
@@ -2,10 +2,13 @@
#include
"difflinenumarea.h"
#include
"ktexteditor_utils.h"
#include
<QMenu>
#include
<QPainter>
#include
<QPainterPath>
#include
<QTextBlock>
#include
<KLocalizedString>
DiffEditor
::
DiffEditor
(
QWidget
*
parent
)
:
QPlainTextEdit
(
parent
)
,
m_lineNumArea
(
new
LineNumArea
(
this
))
...
...
@@ -62,6 +65,29 @@ void DiffEditor::resizeEvent(QResizeEvent *event)
updateLineNumAreaGeometry
();
}
void
DiffEditor
::
contextMenuEvent
(
QContextMenuEvent
*
e
)
{
auto
menu
=
createStandardContextMenu
();
QAction
*
before
=
nullptr
;
if
(
!
menu
->
actions
().
isEmpty
())
before
=
menu
->
actions
().
constFirst
();
auto
a
=
new
QAction
(
i18n
(
"Change Style"
));
auto
styleMenu
=
new
QMenu
(
this
);
styleMenu
->
addAction
(
i18n
(
"Side By Side"
),
this
,
[
this
]
{
Q_EMIT
switchStyle
(
SideBySide
);
});
styleMenu
->
addAction
(
i18n
(
"Unified"
),
this
,
[
this
]
{
Q_EMIT
switchStyle
(
Unified
);
});
styleMenu
->
addAction
(
i18n
(
"Raw"
),
this
,
[
this
]
{
Q_EMIT
switchStyle
(
Raw
);
});
a
->
setMenu
(
styleMenu
);
menu
->
insertAction
(
before
,
a
);
menu
->
exec
(
mapToGlobal
(
e
->
pos
()));
}
void
DiffEditor
::
updateLineNumberArea
(
const
QRect
&
rect
,
int
dy
)
{
if
(
dy
)
...
...
apps/lib/diffeditor.h
View file @
2165e21e
...
...
@@ -20,6 +20,8 @@ struct LineHighlight {
bool
added
;
};
enum
DiffStyle
{
SideBySide
,
Unified
,
Raw
};
class
DiffEditor
:
public
QPlainTextEdit
{
Q_OBJECT
...
...
@@ -30,7 +32,9 @@ public:
void
clearData
()
{
clear
();
m_data
.
clear
();
setLineNumberData
({},
0
);
}
void
appendData
(
const
QVector
<
LineHighlight
>
&
newData
)
{
...
...
@@ -42,6 +46,7 @@ public:
protected:
void
resizeEvent
(
QResizeEvent
*
event
)
override
;
void
paintEvent
(
QPaintEvent
*
e
)
override
;
void
contextMenuEvent
(
QContextMenuEvent
*
e
)
override
;
private:
const
LineHighlight
*
dataForLine
(
int
line
);
...
...
@@ -49,6 +54,7 @@ private:
void
updateLineNumAreaGeometry
();
void
updateLineNumberAreaWidth
(
int
);
void
updateDiffColors
(
bool
darkMode
);
void
onContextMenuRequest
();
QVector
<
LineHighlight
>
m_data
;
QColor
red1
;
...
...
@@ -56,6 +62,9 @@ private:
QColor
green1
;
QColor
green2
;
class
LineNumArea
*
const
m_lineNumArea
;
Q_SIGNALS:
void
switchStyle
(
int
);
};
#endif
apps/lib/diffwidget.cpp
View file @
2165e21e
...
...
@@ -57,15 +57,49 @@ DiffWidget::DiffWidget(QWidget *parent)
const
QSignalBlocker
b
(
m_right
);
m_left
->
verticalScrollBar
()
->
setValue
(
v
);
});
for
(
auto
*
e
:
{
m_left
,
m_right
})
{
connect
(
e
,
&
DiffEditor
::
switchStyle
,
this
,
&
DiffWidget
::
handleStyleChange
);
}
}
void
DiffWidget
::
diffDocs
(
KTextEditor
::
Document
*
l
,
KTextEditor
::
Document
*
r
)
void
DiffWidget
::
handleStyleChange
(
int
newStyle
)
{
if
(
newStyle
==
m_style
)
{
return
;
}
m_style
=
(
DiffStyle
)
newStyle
;
m_left
->
clearData
();
m_right
->
clearData
();
if
(
m_style
==
SideBySide
)
{
m_left
->
setVisible
(
true
);
m_right
->
setVisible
(
true
);
openDiff
(
m_rawDiff
);
}
else
if
(
m_style
==
Unified
)
{
m_left
->
setVisible
(
true
);
m_right
->
setVisible
(
false
);
openDiff
(
m_rawDiff
);
}
else
if
(
m_style
==
Raw
)
{
m_left
->
setVisible
(
true
);
m_right
->
setVisible
(
false
);
openDiff
(
m_rawDiff
);
}
else
{
qWarning
()
<<
"Unexpected diff style value: "
<<
newStyle
;
Q_UNREACHABLE
();
}
}
void
DiffWidget
::
clearData
()
{
m_left
->
clear
();
m_right
->
clear
();
m_left
->
clearData
();
m_right
->
clearData
();
m_rawDiff
.
clear
();
}
void
DiffWidget
::
diffDocs
(
KTextEditor
::
Document
*
l
,
KTextEditor
::
Document
*
r
)
{
clearData
();
const
auto
&
repo
=
KTextEditor
::
Editor
::
instance
()
->
repository
();
const
auto
def
=
repo
.
definitionForMimeType
(
l
->
mimeType
());
if
(
l
->
mimeType
()
==
r
->
mimeType
())
{
...
...
@@ -158,9 +192,10 @@ static std::pair<Change, Change> inlineDiff(QStringView l, QStringView r)
// Struct representing a changed line in hunk
struct
HunkChangedLine
{
HunkChangedLine
(
const
QString
&
ln
,
int
lineNum
)
HunkChangedLine
(
const
QString
&
ln
,
int
lineNum
,
bool
isAdd
)
:
line
(
ln
)
,
lineNo
(
lineNum
)
,
added
(
isAdd
)
{
}
// Line Text
...
...
@@ -168,6 +203,7 @@ struct HunkChangedLine {
// Line Number in text editor (not hunk)
int
lineNo
;
// Which portion of the line changed (len, pos)
bool
added
;
Change
c
=
{
-
1
,
-
1
};
};
using
HunkChangedLines
=
std
::
vector
<
HunkChangedLine
>
;
...
...
@@ -191,7 +227,7 @@ markInlineDiffs(HunkChangedLines &hunkChangedLinesA, HunkChangedLines &hunkChang
for
(
int
i
=
0
;
i
<
(
int
)
hunkChangedLines
.
size
();
++
i
)
{
auto
&
change
=
hunkChangedLines
[
i
];
for
(
int
j
=
hlts
.
size
()
-
1
;
j
>=
0
;
--
j
)
{
if
(
hlts
.
at
(
j
).
line
==
change
.
lineNo
)
{
if
(
hlts
.
at
(
j
).
line
==
change
.
lineNo
&&
hlts
.
at
(
j
).
added
==
change
.
added
)
{
hlts
[
j
].
changes
.
push_back
(
change
.
c
);
break
;
}
else
if
(
hlts
.
at
(
j
).
line
<
change
.
lineNo
)
{
...
...
@@ -306,7 +342,7 @@ void DiffWidget::parseAndShowDiff(const QByteArray &raw)
lineNumsB
.
append
(
tgtLine
++
);
right
.
append
(
l
);
hunkChangedLinesB
.
emplace_back
(
l
,
lineB
);
hunkChangedLinesB
.
emplace_back
(
l
,
lineB
,
h
.
added
);
// lineNo++;
lineB
++
;
...
...
@@ -321,7 +357,7 @@ void DiffWidget::parseAndShowDiff(const QByteArray &raw)
leftHlts
.
push_back
(
h
);
lineNumsA
.
append
(
srcLine
++
);
left
.
append
(
l
);
hunkChangedLinesA
.
emplace_back
(
l
,
lineA
);
hunkChangedLinesA
.
emplace_back
(
l
,
lineA
,
h
.
added
);
lineA
++
;
}
else
if
(
l
.
startsWith
(
QStringLiteral
(
"@@ "
))
&&
HUNK_HEADER_RE
.
match
(
l
).
hasMatch
())
{
...
...
@@ -379,16 +415,173 @@ void DiffWidget::parseAndShowDiff(const QByteArray &raw)
}
}
void
DiffWidget
::
parseAndShowDiffUnified
(
const
QByteArray
&
raw
)
{
// printf("show diff:\n%s\n================================", raw.constData());
const
QStringList
text
=
QString
::
fromUtf8
(
raw
).
replace
(
QStringLiteral
(
"
\r\n
"
),
QStringLiteral
(
"
\n
"
)).
split
(
QLatin1Char
(
'\n'
),
Qt
::
SkipEmptyParts
);
static
const
QRegularExpression
HUNK_HEADER_RE
(
QStringLiteral
(
"^@@ -([0-9,]+)
\\
+([0-9,]+) @@(.*)"
));
static
const
QRegularExpression
DIFF_FILENAME_RE
(
QStringLiteral
(
"^[-+]{3} [ab]/(.*)"
));
// Actual lines that will get added to the text editor
QStringList
lines
;
// Highlighting data for modified lines
QVector
<
LineHighlight
>
hlts
;
// Line numbers that will be shown in the editor
QVector
<
int
>
lineNums
;
// Changed lines of hunk, used to determine differences between two lines
HunkChangedLines
hunkChangedLinesA
;
HunkChangedLines
hunkChangedLinesB
;
QSet
<
QString
>
mimeTypes
;
QString
srcFile
;
QString
tgtFile
;
int
maxLineNoFound
=
0
;
int
lineNo
=
0
;
for
(
int
i
=
0
;
i
<
text
.
size
();
++
i
)
{
const
QString
&
line
=
text
.
at
(
i
);
auto
match
=
DIFF_FILENAME_RE
.
match
(
line
);
if
(
match
.
hasMatch
())
{
if
(
line
.
startsWith
(
QLatin1Char
(
'-'
)))
{
srcFile
=
match
.
captured
(
1
);
}
else
if
(
line
.
startsWith
(
QLatin1Char
(
'+'
)))
{
tgtFile
=
match
.
captured
(
1
);
}
if
(
!
match
.
captured
(
1
).
isEmpty
())
{
mimeTypes
.
insert
(
QMimeDatabase
().
mimeTypeForFile
(
match
.
captured
(
1
),
QMimeDatabase
::
MatchExtension
).
name
());
}
continue
;
}
match
=
HUNK_HEADER_RE
.
match
(
line
);
if
(
!
match
.
hasMatch
())
continue
;
const
auto
oldRange
=
parseRange
(
match
.
captured
(
1
));
const
auto
newRange
=
parseRange
(
match
.
captured
(
2
));
const
QString
headingLeft
=
QStringLiteral
(
"@@ "
)
+
match
.
captured
(
1
)
+
match
.
captured
(
3
)
/* + QStringLiteral(" ") + srcFile*/
;
const
QString
headingRight
=
QStringLiteral
(
"@@ "
)
+
match
.
captured
(
2
)
+
match
.
captured
(
3
)
/* + QStringLiteral(" ") + tgtFile*/
;
lines
.
push_back
(
line
);
lineNums
.
append
(
-
1
);
lineNo
++
;
hunkChangedLinesA
.
clear
();
hunkChangedLinesB
.
clear
();
int
srcLine
=
oldRange
.
first
;
const
int
oldCount
=
oldRange
.
second
;
int
tgtLine
=
newRange
.
first
;
const
int
newCount
=
newRange
.
second
;
maxLineNoFound
=
qMax
(
qMax
(
srcLine
+
oldCount
,
tgtLine
+
newCount
),
maxLineNoFound
);
for
(
int
j
=
i
+
1
;
j
<
text
.
size
();
j
++
)
{
QString
l
=
text
.
at
(
j
);
if
(
l
.
startsWith
(
QLatin1Char
(
' '
)))
{
markInlineDiffs
(
hunkChangedLinesA
,
hunkChangedLinesB
,
hlts
,
hlts
);
l
=
l
.
mid
(
1
);
lines
.
append
(
l
);
lineNums
.
append
(
srcLine
++
);
tgtLine
++
;
lineNo
++
;
// lineB++;
}
else
if
(
l
.
startsWith
(
QLatin1Char
(
'+'
)))
{
// qDebug() << "- line";
l
=
l
.
mid
(
1
);
LineHighlight
h
;
h
.
line
=
lineNo
;
h
.
added
=
true
;
h
.
changes
.
push_back
({
0
,
l
.
size
()});
lines
.
append
(
l
);
hlts
.
push_back
(
h
);
lineNums
.
append
(
tgtLine
++
);
hunkChangedLinesB
.
emplace_back
(
l
,
lineNo
,
h
.
added
);
lineNo
++
;
}
else
if
(
l
.
startsWith
(
QLatin1Char
(
'-'
)))
{
l
=
l
.
mid
(
1
);
LineHighlight
h
;
h
.
line
=
lineNo
;
h
.
added
=
false
;
h
.
changes
.
push_back
({
0
,
l
.
size
()});
hlts
.
push_back
(
h
);
lineNums
.
append
(
srcLine
++
);
lines
.
append
(
l
);
hunkChangedLinesA
.
emplace_back
(
l
,
lineNo
,
h
.
added
);
lineNo
++
;
}
else
if
(
l
.
startsWith
(
QStringLiteral
(
"@@ "
))
&&
HUNK_HEADER_RE
.
match
(
l
).
hasMatch
())
{
i
=
j
-
1
;
markInlineDiffs
(
hunkChangedLinesA
,
hunkChangedLinesB
,
hlts
,
hlts
);
// add line number for current line
lineNums
.
append
(
-
1
);
// add new line
lines
.
append
(
QString
());
lineNo
+=
1
;
break
;
}
else
if
(
l
.
startsWith
(
QStringLiteral
(
"diff --git "
)))
{
// Start of a new file
markInlineDiffs
(
hunkChangedLinesA
,
hunkChangedLinesB
,
hlts
,
hlts
);
// add new line
lines
.
append
(
QString
());
lineNo
+=
1
;
break
;
}
if
(
j
+
1
>=
text
.
size
())
{
markInlineDiffs
(
hunkChangedLinesA
,
hunkChangedLinesB
,
hlts
,
hlts
);
i
=
j
;
// ensure outer loop also exits after this
}
}
}
m_left
->
appendData
(
hlts
);
m_left
->
appendPlainText
(
lines
.
join
(
QLatin1Char
(
'\n'
)));
m_left
->
setLineNumberData
(
lineNums
,
maxLineNoFound
);
// Only do highlighting if there is one mimetype found, multiple different files not supported
if
(
mimeTypes
.
size
()
==
1
)
{
const
auto
def
=
KTextEditor
::
Editor
::
instance
()
->
repository
().
definitionForMimeType
(
*
mimeTypes
.
begin
());
leftHl
->
setDefinition
(
def
);
}
else
{
const
auto
def
=
KTextEditor
::
Editor
::
instance
()
->
repository
().
definitionForName
(
QStringLiteral
(
"None"
));
leftHl
->
setDefinition
(
def
);
}
}
void
DiffWidget
::
openDiff
(
const
QByteArray
&
raw
)
{
parseAndShowDiff
(
raw
);
if
(
m_style
==
SideBySide
)
{
parseAndShowDiff
(
raw
);
}
else
if
(
m_style
==
Unified
)
{
parseAndShowDiffUnified
(
raw
);
}
else
if
(
m_style
==
Raw
)
{
m_left
->
setPlainText
(
QString
::
fromUtf8
(
raw
));
leftHl
->
setDefinition
(
KTextEditor
::
Editor
::
instance
()
->
repository
().
definitionForName
(
QStringLiteral
(
"Diff"
)));
}
m_rawDiff
=
raw
;
m_left
->
verticalScrollBar
()
->
setValue
(
0
);
m_right
->
verticalScrollBar
()
->
setValue
(
0
);
}
void
DiffWidget
::
onTextReceived
(
const
QByteArray
&
raw
)
{
parseAndShowDiff
(
raw
);
if
(
m_style
==
SideBySide
)
{
parseAndShowDiff
(
raw
);
}
else
if
(
m_style
==
Unified
)
{
parseAndShowDiffUnified
(
raw
);
}
else
if
(
m_style
==
Raw
)
{
m_left
->
appendPlainText
(
QString
::
fromUtf8
(
raw
));
}
m_rawDiff
+=
raw
;
}
void
DiffWidget
::
onError
(
const
QByteArray
&
/*error*/
,
int
/*code*/
)
...
...
apps/lib/diffwidget.h
View file @
2165e21e
...
...
@@ -3,6 +3,7 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include
"diffeditor.h"
#include
<QPlainTextEdit>
#include
<QWidget>
...
...
@@ -29,12 +30,17 @@ public:
}
private:
void
clearData
();
void
handleStyleChange
(
int
);
void
onTextReceived
(
const
QByteArray
&
text
);
void
onError
(
const
QByteArray
&
error
,
int
code
);
void
parseAndShowDiff
(
const
QByteArray
&
raw
);
void
parseAndShowDiffUnified
(
const
QByteArray
&
raw
);
class
DiffEditor
*
m_left
;
class
DiffEditor
*
m_right
;
KSyntaxHighlighting
::
SyntaxHighlighter
*
leftHl
;
KSyntaxHighlighting
::
SyntaxHighlighter
*
rightHl
;
DiffStyle
m_style
=
SideBySide
;
QByteArray
m_rawDiff
;
// Raw diff saved as is
};
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment