Skip to content
GitLab
Menu
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
efa557be
Commit
efa557be
authored
Feb 13, 2021
by
Kåre Särs
Browse files
First commit for a git blame plugin
parent
6fd975c5
Changes
6
Hide whitespace changes
Inline
Side-by-side
addons/CMakeLists.txt
View file @
efa557be
...
...
@@ -8,6 +8,7 @@ ecm_optional_add_subdirectory(externaltools)
ecm_optional_add_subdirectory
(
filebrowser
)
ecm_optional_add_subdirectory
(
filetree
)
ecm_optional_add_subdirectory
(
gdbplugin
)
ecm_optional_add_subdirectory
(
git-blame
)
# Inline git-blame viewer
ecm_optional_add_subdirectory
(
kate-ctags
)
ecm_optional_add_subdirectory
(
katebuild-plugin
)
ecm_optional_add_subdirectory
(
katesql
)
...
...
addons/git-blame/CMakeLists.txt
0 → 100644
View file @
efa557be
add_library
(
kategitblameplugin MODULE
""
)
target_compile_definitions
(
kategitblameplugin PRIVATE TRANSLATION_DOMAIN=
"kategitblameplugin"
)
target_link_libraries
(
kategitblameplugin PRIVATE KF5::TextEditor
)
target_sources
(
kategitblameplugin
PRIVATE
kategitblameplugin.cpp
)
kcoreaddons_desktop_to_json
(
kategitblameplugin kategitblameplugin.desktop
)
install
(
TARGETS kategitblameplugin DESTINATION
${
KDE_INSTALL_PLUGINDIR
}
/ktexteditor
)
addons/git-blame/Messages.sh
0 → 100644
View file @
efa557be
#! /bin/sh
$EXTRACTRC
*
.rc
>>
rc.cpp
$XGETTEXT
*
.cpp
-o
$podir
/kategitblameplugin.pot
addons/git-blame/kategitblameplugin.cpp
0 → 100644
View file @
efa557be
/*
SPDX-FileCopyrightText: 2021 Kåre Särs <kare.sars@iki.fi>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kategitblameplugin.h"
#include <algorithm>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KSharedConfig>
#include <KTextEditor/Document>
#include <KTextEditor/InlineNoteInterface>
#include <KTextEditor/InlineNoteProvider>
#include <KTextEditor/MainWindow>
#include <KTextEditor/View>
#include <QUrl>
#include <QDir>
#include <QFontMetricsF>
#include <QHash>
#include <QPainter>
#include <QRegularExpression>
#include <QVariant>
GitBlameInlineNoteProvider
::
GitBlameInlineNoteProvider
(
KTextEditor
::
Document
*
doc
,
KateGitBlamePlugin
*
plugin
)
:
m_doc
(
doc
)
,
m_plugin
(
plugin
)
{
for
(
auto
view
:
m_doc
->
views
())
{
qobject_cast
<
KTextEditor
::
InlineNoteInterface
*>
(
view
)
->
registerInlineNoteProvider
(
this
);
}
connect
(
m_doc
,
&
KTextEditor
::
Document
::
viewCreated
,
this
,
[
this
](
KTextEditor
::
Document
*
,
KTextEditor
::
View
*
view
)
{
qobject_cast
<
KTextEditor
::
InlineNoteInterface
*>
(
view
)
->
registerInlineNoteProvider
(
this
);
});
// textInserted and textRemoved are emitted per line, then the last line is followed by a textChanged signal
connect
(
m_doc
,
&
KTextEditor
::
Document
::
textInserted
,
this
,
[
/*this*/
](
KTextEditor
::
Document
*
,
const
KTextEditor
::
Cursor
&
/*cur*/
,
const
QString
&
/*str*/
)
{
//qDebug() << cur.line() << str << this;
});
connect
(
m_doc
,
&
KTextEditor
::
Document
::
textRemoved
,
this
,
[
/*this*/
](
KTextEditor
::
Document
*
,
const
KTextEditor
::
Range
&
/*range*/
,
const
QString
&
/*str*/
)
{
//qDebug() << range.start() << str << this;
});
connect
(
m_doc
,
&
KTextEditor
::
Document
::
textChanged
,
this
,
[
/*this*/
](
KTextEditor
::
Document
*
)
{
//qDebug() << this;
});
}
GitBlameInlineNoteProvider
::~
GitBlameInlineNoteProvider
()
{
for
(
auto
view
:
m_doc
->
views
())
{
qobject_cast
<
KTextEditor
::
InlineNoteInterface
*>
(
view
)
->
unregisterInlineNoteProvider
(
this
);
}
}
QVector
<
int
>
GitBlameInlineNoteProvider
::
inlineNotes
(
int
line
)
const
{
if
(
!
m_plugin
->
hasBlameInfo
())
{
return
QVector
<
int
>
();
}
int
lineLen
=
m_doc
->
line
(
line
).
size
();
for
(
const
auto
view
:
m_doc
->
views
())
{
if
(
view
->
cursorPosition
().
line
()
==
line
)
{
return
QVector
<
int
>
{
lineLen
+
2
};
}
}
return
QVector
<
int
>
();
}
QSize
GitBlameInlineNoteProvider
::
inlineNoteSize
(
const
KTextEditor
::
InlineNote
&
note
)
const
{
return
QSize
(
note
.
lineHeight
()
*
10
,
note
.
lineHeight
());
}
void
GitBlameInlineNoteProvider
::
paintInlineNote
(
const
KTextEditor
::
InlineNote
&
note
,
QPainter
&
painter
)
const
{
auto
penColor
=
QColor
(
"black"
);
penColor
.
setAlpha
(
note
.
underMouse
()
?
130
:
90
);
painter
.
setPen
(
penColor
);
painter
.
setBrush
(
penColor
);
QFont
font
=
note
.
font
();
painter
.
setFont
(
font
);
const
QFontMetrics
fm
(
note
.
font
());
int
lineNr
=
note
.
position
().
line
();
const
KateGitBlameInfo
&
info
=
m_plugin
->
blameInfo
(
lineNr
,
m_doc
->
line
(
lineNr
));
QString
text
=
QStringLiteral
(
"%1: %2"
).
arg
(
info
.
name
,
info
.
date
);
QRect
rectangle
=
fm
.
boundingRect
(
text
);
rectangle
.
moveTo
(
0
,
0
);
painter
.
drawText
(
rectangle
,
text
);
}
void
GitBlameInlineNoteProvider
::
inlineNoteActivated
(
const
KTextEditor
::
InlineNote
&
note
,
Qt
::
MouseButtons
buttons
,
const
QPoint
&
point
)
{
qDebug
()
<<
"pos:"
<<
note
.
position
()
<<
buttons
<<
point
;
}
K_PLUGIN_FACTORY_WITH_JSON
(
KateGitBlamePluginFactory
,
"kategitblameplugin.json"
,
registerPlugin
<
KateGitBlamePlugin
>
();)
KateGitBlamePlugin
::
KateGitBlamePlugin
(
QObject
*
parent
,
const
QList
<
QVariant
>
&
)
:
KTextEditor
::
Plugin
(
parent
)
,
m_blameInfoProc
(
this
)
{
m_blameInfoProc
.
setOutputChannelMode
(
KProcess
::
SeparateChannels
);
connect
(
&
m_blameInfoProc
,
static_cast
<
void
(
QProcess
::*
)(
int
,
QProcess
::
ExitStatus
)
>
(
&
QProcess
::
finished
),
this
,
&
KateGitBlamePlugin
::
finished
);
}
KateGitBlamePlugin
::~
KateGitBlamePlugin
()
{
qDeleteAll
(
m_inlineNoteProviders
);
}
QObject
*
KateGitBlamePlugin
::
createView
(
KTextEditor
::
MainWindow
*
mainWindow
)
{
m_mainWindow
=
mainWindow
;
for
(
auto
view
:
m_mainWindow
->
views
())
{
addDocument
(
view
->
document
());
}
connect
(
m_mainWindow
,
&
KTextEditor
::
MainWindow
::
viewCreated
,
this
,
[
this
](
KTextEditor
::
View
*
view
)
{
addDocument
(
view
->
document
());
});
connect
(
m_mainWindow
,
&
KTextEditor
::
MainWindow
::
viewChanged
,
this
,
&
KateGitBlamePlugin
::
viewChanged
);
return
nullptr
;
}
void
KateGitBlamePlugin
::
addDocument
(
KTextEditor
::
Document
*
doc
)
{
if
(
!
m_inlineNoteProviders
.
contains
(
doc
))
{
m_inlineNoteProviders
.
insert
(
doc
,
new
GitBlameInlineNoteProvider
(
doc
,
this
));
}
connect
(
doc
,
&
KTextEditor
::
Document
::
destroyed
,
this
,
[
this
,
doc
]()
{
m_inlineNoteProviders
.
remove
(
doc
);
});
}
void
KateGitBlamePlugin
::
viewChanged
(
KTextEditor
::
View
*
view
)
{
m_blameInfo
.
clear
();
if
(
view
==
nullptr
||
view
->
document
()
==
nullptr
)
{
return
;
}
if
(
m_blameInfoProc
.
state
()
!=
QProcess
::
NotRunning
)
{
// Wait for the previous process to be done...
return
;
}
m_blameInfoView
=
view
;
QUrl
url
=
view
->
document
()
->
url
();
QString
fileName
{
url
.
fileName
()};
QDir
dir
{
url
.
toLocalFile
()};
dir
.
cdUp
();
QString
shellCmd
=
QStringLiteral
(
"git blame ./%1"
).
arg
(
fileName
);
m_blameInfoProc
.
setWorkingDirectory
(
dir
.
absolutePath
());
m_blameInfoProc
.
setShellCommand
(
shellCmd
);
m_blameInfoProc
.
start
();
}
void
KateGitBlamePlugin
::
finished
(
int
/*exitCode*/
,
QProcess
::
ExitStatus
/*exitStatus*/
)
{
QString
stdErr
=
QString
::
fromUtf8
(
m_blameInfoProc
.
readAllStandardError
());
const
QStringList
stdOut
=
QString
::
fromUtf8
(
m_blameInfoProc
.
readAllStandardOutput
()).
split
(
QLatin1Char
(
'\n'
));
// check if the git process was running for a previous document when the view changed.
// if that is the case re-trigger the process and skip this data
if
(
m_blameInfoView
!=
m_mainWindow
->
activeView
())
{
viewChanged
(
m_mainWindow
->
activeView
());
return
;
}
const
static
QRegularExpression
lineReg
(
QStringLiteral
(
"(
\\
S+)[^
\\
(]+
\\
((.*)
\\
s+(
\\
d
\\
d
\\
d
\\
d-
\\
d
\\
d-
\\
d
\\
d
\\
d
\\
d:
\\
d
\\
d:
\\
d
\\
d)[^
\\
)]+
\\
)
\\
s(.*)"
));
m_blameInfo
.
clear
();
for
(
const
auto
&
line
:
stdOut
)
{
const
QRegularExpressionMatch
match
=
lineReg
.
match
(
line
);
if
(
match
.
hasMatch
())
{
m_blameInfo
.
append
({
match
.
captured
(
1
),
match
.
captured
(
2
).
trimmed
(),
match
.
captured
(
3
),
match
.
captured
(
4
)
});
}
}
}
bool
KateGitBlamePlugin
::
hasBlameInfo
()
const
{
return
!
m_blameInfo
.
isEmpty
();
}
const
KateGitBlameInfo
&
KateGitBlamePlugin
::
blameInfo
(
int
lineNr
,
const
QStringView
&
lineText
)
{
static
const
KateGitBlameInfo
dummy
{
QStringLiteral
(
"hash"
),
QStringLiteral
(
"Not Committed Yet"
),
QStringLiteral
(
""
),
QStringLiteral
(
""
)};
int
adjustedLineNr
=
lineNr
+
m_lineOffset
;
if
(
adjustedLineNr
>=
0
&&
adjustedLineNr
<
m_blameInfo
.
size
())
{
if
(
m_blameInfo
[
adjustedLineNr
].
line
==
lineText
)
{
return
m_blameInfo
.
at
(
adjustedLineNr
);
}
}
// FIXME search for the matching line
// search for the line 100 lines before and after until it matches
m_lineOffset
=
0
;
while
(
m_lineOffset
<
100
&&
lineNr
+
m_lineOffset
<
m_blameInfo
.
size
())
{
if
(
m_blameInfo
[
lineNr
+
m_lineOffset
].
line
==
lineText
)
{
return
m_blameInfo
.
at
(
lineNr
+
m_lineOffset
);
}
m_lineOffset
++
;
}
m_lineOffset
=
0
;
while
(
m_lineOffset
>
-
100
&&
lineNr
+
m_lineOffset
>=
0
)
{
if
(
m_blameInfo
[
lineNr
+
m_lineOffset
].
line
==
lineText
)
{
return
m_blameInfo
.
at
(
lineNr
+
m_lineOffset
);
}
m_lineOffset
--
;
}
return
dummy
;
}
void
KateGitBlamePlugin
::
readConfig
()
{
}
#include "kategitblameplugin.moc"
addons/git-blame/kategitblameplugin.desktop
0 → 100644
View file @
efa557be
[Desktop Entry]
Type=Service
ServiceTypes=KTextEditor/Plugin
X-KDE-Library=kategitblameplugin
Name=Git Blame
Name[x-test]=xxGit Blamexx
Comment=Adds an inline git-blame preview at the end of the line
Comment[x-test]=xxAdds an inline git-blame preview at the end of the linexx
addons/git-blame/kategitblameplugin.h
0 → 100644
View file @
efa557be
/*
SPDX-FileCopyrightText: 2021 Kåre Särs <kare.sars@iki.fi>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KATE_COLORPICKER_H
#define KATE_COLORPICKER_H
#include <KTextEditor/ConfigPage>
#include <KTextEditor/InlineNoteProvider>
#include <KTextEditor/MainWindow>
#include <KTextEditor/Plugin>
#include <KProcess>
#include <QHash>
#include <QList>
#include <QRegularExpression>
#include <QVariant>
#include <QVector>
struct
KateGitBlameInfo
{
QString
commitHash
;
QString
name
;
QString
date
;
QString
line
;
};
class
KateGitBlamePlugin
;
class
GitBlameInlineNoteProvider
:
public
KTextEditor
::
InlineNoteProvider
{
Q_OBJECT
public:
GitBlameInlineNoteProvider
(
KTextEditor
::
Document
*
doc
,
KateGitBlamePlugin
*
plugin
);
~
GitBlameInlineNoteProvider
();
QVector
<
int
>
inlineNotes
(
int
line
)
const
override
;
QSize
inlineNoteSize
(
const
KTextEditor
::
InlineNote
&
note
)
const
override
;
void
paintInlineNote
(
const
KTextEditor
::
InlineNote
&
note
,
QPainter
&
painter
)
const
override
;
void
inlineNoteActivated
(
const
KTextEditor
::
InlineNote
&
note
,
Qt
::
MouseButtons
buttons
,
const
QPoint
&
globalPos
)
override
;
private:
KTextEditor
::
Document
*
m_doc
;
KateGitBlamePlugin
*
m_plugin
;
};
class
KateGitBlamePlugin
:
public
KTextEditor
::
Plugin
{
Q_OBJECT
public:
explicit
KateGitBlamePlugin
(
QObject
*
parent
=
nullptr
,
const
QList
<
QVariant
>
&
=
QList
<
QVariant
>
());
~
KateGitBlamePlugin
()
override
;
QObject
*
createView
(
KTextEditor
::
MainWindow
*
mainWindow
)
override
;
const
KateGitBlameInfo
&
blameInfo
(
int
lineNr
,
const
QStringView
&
lineText
);
bool
hasBlameInfo
()
const
;
void
readConfig
();
private
Q_SLOTS
:
void
viewChanged
(
KTextEditor
::
View
*
view
);
void
finished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
);
private:
void
addDocument
(
KTextEditor
::
Document
*
doc
);
KTextEditor
::
MainWindow
*
m_mainWindow
;
QHash
<
KTextEditor
::
Document
*
,
GitBlameInlineNoteProvider
*>
m_inlineNoteProviders
;
KProcess
m_blameInfoProc
;
QVector
<
KateGitBlameInfo
>
m_blameInfo
;
KTextEditor
::
View
*
m_blameInfoView
;
int
m_lineOffset
{
0
};
};
#endif // KATE_COLORPICKER_H
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a 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