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
c13307f5
Commit
c13307f5
authored
Feb 16, 2021
by
Kåre Särs
Browse files
Add gitblametooltip to show the commit info
parent
efa557be
Changes
5
Hide whitespace changes
Inline
Side-by-side
addons/git-blame/CMakeLists.txt
View file @
c13307f5
...
...
@@ -6,6 +6,7 @@ target_sources(
kategitblameplugin
PRIVATE
kategitblameplugin.cpp
gitblametooltip.cpp
)
kcoreaddons_desktop_to_json
(
kategitblameplugin kategitblameplugin.desktop
)
...
...
addons/git-blame/gitblametooltip.cpp
0 → 100644
View file @
c13307f5
/* SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: MIT
*/
#include "gitblametooltip.h"
#include <QApplication>
#include <QDebug>
#include <QEvent>
#include <QFontMetrics>
#include <QLabel>
#include <QMouseEvent>
#include <QScreen>
#include <QString>
#include <QTextBrowser>
#include <QTimer>
#include <QScrollBar>
#include <KTextEditor/ConfigInterface>
#include <KTextEditor/Editor>
#include <KTextEditor/View>
#include <KSyntaxHighlighting/AbstractHighlighter>
#include <KSyntaxHighlighting/Definition>
#include <KSyntaxHighlighting/Format>
#include <KSyntaxHighlighting/Repository>
#include <KSyntaxHighlighting/State>
using
KSyntaxHighlighting
::
AbstractHighlighter
;
using
KSyntaxHighlighting
::
Format
;
static
QString
toHtmlRgbaString
(
const
QColor
&
color
)
{
if
(
color
.
alpha
()
==
0xFF
)
return
color
.
name
();
QString
rgba
=
QStringLiteral
(
"rgba("
);
rgba
.
append
(
QString
::
number
(
color
.
red
()));
rgba
.
append
(
QLatin1Char
(
','
));
rgba
.
append
(
QString
::
number
(
color
.
green
()));
rgba
.
append
(
QLatin1Char
(
','
));
rgba
.
append
(
QString
::
number
(
color
.
blue
()));
rgba
.
append
(
QLatin1Char
(
','
));
// this must be alphaF
rgba
.
append
(
QString
::
number
(
color
.
alphaF
()));
rgba
.
append
(
QLatin1Char
(
')'
));
return
rgba
;
}
class
HtmlHl
:
public
AbstractHighlighter
{
public:
HtmlHl
()
:
out
(
&
outputString
)
{
}
void
setText
(
const
QString
&
txt
)
{
text
=
txt
;
QTextStream
in
(
&
text
);
out
.
reset
();
outputString
.
clear
();
bool
inDiff
=
false
;
KSyntaxHighlighting
::
State
state
;
while
(
!
in
.
atEnd
())
{
currentLine
=
in
.
readLine
();
// allow empty lines in code blocks, no ruler here
if
(
!
inDiff
&&
currentLine
.
isEmpty
())
{
out
<<
"<hr>"
;
continue
;
}
// diff block
if
(
!
inDiff
&&
currentLine
.
startsWith
(
QLatin1String
(
"diff"
)))
{
inDiff
=
true
;
continue
;
}
state
=
highlightLine
(
currentLine
,
state
);
out
<<
"
\n
<br>"
;
}
}
QString
html
()
const
{
// while (!out.atEnd())
// qWarning() << out.readLine();
return
outputString
;
}
protected:
void
applyFormat
(
int
offset
,
int
length
,
const
Format
&
format
)
override
{
if
(
!
length
)
return
;
QString
formatOutput
;
if
(
format
.
hasTextColor
(
theme
()))
{
formatOutput
=
toHtmlRgbaString
(
format
.
textColor
(
theme
()));
}
if
(
!
formatOutput
.
isEmpty
())
{
out
<<
"<span style=
\"
color:"
<<
formatOutput
<<
"
\"
>"
;
}
out
<<
currentLine
.
mid
(
offset
,
length
).
toHtmlEscaped
();
if
(
!
formatOutput
.
isEmpty
())
{
out
<<
"</span>"
;
}
}
private:
QString
text
;
QString
currentLine
;
QString
outputString
;
QTextStream
out
;
};
class
Tooltip
:
public
QTextBrowser
{
Q_OBJECT
public:
static
Tooltip
*
self
()
{
static
Tooltip
instance
;
return
&
instance
;
}
void
setTooltipText
(
const
QString
&
text
)
{
if
(
text
.
isEmpty
())
return
;
m_htmlHl
.
setText
(
text
);
setHtml
(
m_htmlHl
.
html
());
}
void
setView
(
KTextEditor
::
View
*
view
)
{
// view changed?
// => update definition
// => update font
if
(
view
!=
m_view
)
{
if
(
m_view
&&
m_view
->
focusProxy
())
{
m_view
->
focusProxy
()
->
removeEventFilter
(
this
);
}
m_view
=
view
;
m_htmlHl
.
setDefinition
(
m_syntaxHlRepo
.
definitionForName
(
QStringLiteral
(
"Diff"
)));
updateFont
();
if
(
m_view
&&
m_view
->
focusProxy
())
{
m_view
->
focusProxy
()
->
installEventFilter
(
this
);
}
}
}
Tooltip
(
QWidget
*
parent
=
nullptr
)
:
QTextBrowser
(
parent
)
{
setWindowFlags
(
Qt
::
FramelessWindowHint
|
Qt
::
BypassGraphicsProxyWidget
|
Qt
::
ToolTip
);
document
()
->
setDocumentMargin
(
5
);
setFrameStyle
(
QFrame
::
Box
|
QFrame
::
Raised
);
connect
(
&
m_hideTimer
,
&
QTimer
::
timeout
,
this
,
&
Tooltip
::
hideTooltip
);
setHorizontalScrollBarPolicy
(
Qt
::
ScrollBarAsNeeded
);
setVerticalScrollBarPolicy
(
Qt
::
ScrollBarAsNeeded
);
auto
updateColors
=
[
this
](
KTextEditor
::
Editor
*
e
)
{
auto
theme
=
e
->
theme
();
m_htmlHl
.
setTheme
(
theme
);
auto
pal
=
palette
();
const
QColor
bg
=
theme
.
editorColor
(
KSyntaxHighlighting
::
Theme
::
BackgroundColor
);
pal
.
setColor
(
QPalette
::
Base
,
bg
);
const
QColor
normal
=
theme
.
textColor
(
KSyntaxHighlighting
::
Theme
::
Normal
);
pal
.
setColor
(
QPalette
::
Text
,
normal
);
setPalette
(
pal
);
updateFont
();
};
updateColors
(
KTextEditor
::
Editor
::
instance
());
connect
(
KTextEditor
::
Editor
::
instance
(),
&
KTextEditor
::
Editor
::
configChanged
,
this
,
updateColors
);
}
bool
eventFilter
(
QObject
*
,
QEvent
*
e
)
override
{
switch
(
e
->
type
())
{
case
QEvent
::
KeyPress
:
case
QEvent
::
KeyRelease
:
case
QEvent
::
WindowActivate
:
case
QEvent
::
WindowDeactivate
:
hideTooltip
();
break
;
default:
break
;
}
return
false
;
}
void
updateFont
()
{
if
(
!
m_view
)
return
;
auto
ciface
=
qobject_cast
<
KTextEditor
::
ConfigInterface
*>
(
m_view
);
auto
font
=
ciface
->
configValue
(
QStringLiteral
(
"font"
)).
value
<
QFont
>
();
setFont
(
font
);
}
Q_SLOT
void
hideTooltip
()
{
close
();
setText
(
QString
());
}
void
fixGeometry
()
{
static
QScrollBar
scrollBar
(
Qt
::
Horizontal
);
QFontMetrics
fm
(
font
());
QSize
size
=
fm
.
size
(
Qt
::
TextSingleLine
,
QStringLiteral
(
"m"
));
int
fontHeight
=
size
.
height
();
size
.
setHeight
(
m_view
->
height
()
-
fontHeight
*
2
-
scrollBar
.
sizeHint
().
height
());
size
.
setWidth
(
qRound
(
m_view
->
width
()
*
0.7
));
resize
(
size
);
QPoint
p
=
m_view
->
mapToGlobal
(
m_view
->
pos
());
p
.
setY
(
p
.
y
()
+
fontHeight
);
p
.
setX
(
p
.
x
()
+
m_view
->
textAreaRect
().
left
()
+
m_view
->
textAreaRect
().
width
()
-
size
.
width
()
-
fontHeight
);
this
->
move
(
p
);
}
protected:
void
showEvent
(
QShowEvent
*
event
)
override
{
m_hideTimer
.
start
(
3000
);
return
QTextBrowser
::
showEvent
(
event
);
}
void
enterEvent
(
QEvent
*
event
)
override
{
inContextMenu
=
false
;
m_hideTimer
.
stop
();
return
QTextBrowser
::
enterEvent
(
event
);
}
void
leaveEvent
(
QEvent
*
event
)
override
{
if
(
!
m_hideTimer
.
isActive
()
&&
!
inContextMenu
)
{
hideTooltip
();
}
return
QTextBrowser
::
leaveEvent
(
event
);
}
void
mouseMoveEvent
(
QMouseEvent
*
event
)
override
{
auto
pos
=
event
->
pos
();
if
(
rect
().
contains
(
pos
))
{
return
QTextBrowser
::
mouseMoveEvent
(
event
);
}
hideTooltip
();
}
void
contextMenuEvent
(
QContextMenuEvent
*
e
)
override
{
inContextMenu
=
true
;
return
QTextBrowser
::
contextMenuEvent
(
e
);
}
private:
bool
inContextMenu
=
false
;
KTextEditor
::
View
*
m_view
;
QTimer
m_hideTimer
;
HtmlHl
m_htmlHl
;
KSyntaxHighlighting
::
Repository
m_syntaxHlRepo
;
};
void
GitBlameTooltip
::
show
(
const
QString
&
text
,
KTextEditor
::
View
*
v
)
{
if
(
text
.
isEmpty
()
||
!
v
||
!
v
->
document
())
{
return
;
}
Tooltip
::
self
()
->
setView
(
v
);
Tooltip
::
self
()
->
setTooltipText
(
text
);
Tooltip
::
self
()
->
fixGeometry
();
Tooltip
::
self
()
->
raise
();
Tooltip
::
self
()
->
show
();
}
#include "gitblametooltip.moc"
addons/git-blame/gitblametooltip.h
0 → 100644
View file @
c13307f5
/* SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: MIT
*/
#ifndef LSPTOOLTIP_H
#define LSPTOOLTIP_H
#include <QPoint>
class
QWidget
;
class
QString
;
class
Tooltip
;
namespace
KTextEditor
{
class
View
;
}
class
GitBlameTooltip
{
public:
static
void
show
(
const
QString
&
text
,
KTextEditor
::
View
*
v
);
};
#endif // LSPTOOLTIP_H
addons/git-blame/kategitblameplugin.cpp
View file @
c13307f5
...
...
@@ -5,6 +5,7 @@
*/
#include "kategitblameplugin.h"
#include "gitblametooltip.h"
#include <algorithm>
...
...
@@ -68,7 +69,7 @@ QVector<int> GitBlameInlineNoteProvider::inlineNotes(int line) const
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
>
{
qMax
(
lineLen
+
2
,
75
)
};
}
}
return
QVector
<
int
>
();
...
...
@@ -76,15 +77,11 @@ QVector<int> GitBlameInlineNoteProvider::inlineNotes(int line) const
QSize
GitBlameInlineNoteProvider
::
inlineNoteSize
(
const
KTextEditor
::
InlineNote
&
note
)
const
{
return
QSize
(
note
.
lineHeight
()
*
1
0
,
note
.
lineHeight
());
return
QSize
(
note
.
lineHeight
()
*
5
0
,
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
());
...
...
@@ -92,16 +89,31 @@ void GitBlameInlineNoteProvider::paintInlineNote(const KTextEditor::InlineNote &
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
);
QString
text
=
QStringLiteral
(
"
%1: %2"
).
arg
(
info
.
name
,
info
.
date
);
QRect
rectangle
=
fm
.
boundingRect
(
text
);
rectangle
.
moveTo
(
0
,
0
);
auto
penColor
=
QColor
(
"black"
);
penColor
.
setAlpha
(
20
);
painter
.
setPen
(
penColor
);
painter
.
setBrush
(
penColor
);
painter
.
drawRect
(
0
,
0
,
rectangle
.
width
(),
note
.
lineHeight
());
penColor
.
setAlpha
(
note
.
underMouse
()
?
130
:
90
);
painter
.
setPen
(
penColor
);
painter
.
setBrush
(
penColor
);
painter
.
drawRect
(
0
,
0
,
1
,
note
.
lineHeight
());
painter
.
drawText
(
rectangle
,
text
);
}
void
GitBlameInlineNoteProvider
::
inlineNoteActivated
(
const
KTextEditor
::
InlineNote
&
note
,
Qt
::
MouseButtons
buttons
,
const
QPoint
&
point
)
{
qDebug
()
<<
"pos:"
<<
note
.
position
()
<<
buttons
<<
point
;
if
((
buttons
&
Qt
::
LeftButton
)
!=
0
)
{
int
lineNr
=
note
.
position
().
line
();
const
KateGitBlameInfo
&
info
=
m_plugin
->
blameInfo
(
lineNr
,
m_doc
->
line
(
lineNr
));
m_plugin
->
showCommitInfo
(
info
.
commitHash
,
point
);
}
}
K_PLUGIN_FACTORY_WITH_JSON
(
KateGitBlamePluginFactory
,
"kategitblameplugin.json"
,
registerPlugin
<
KateGitBlamePlugin
>
();)
...
...
@@ -112,7 +124,10 @@ KateGitBlamePlugin::KateGitBlamePlugin(QObject *parent, const QList<QVariant> &)
{
m_blameInfoProc
.
setOutputChannelMode
(
KProcess
::
SeparateChannels
);
connect
(
&
m_blameInfoProc
,
static_cast
<
void
(
QProcess
::*
)(
int
,
QProcess
::
ExitStatus
)
>
(
&
QProcess
::
finished
),
this
,
&
KateGitBlamePlugin
::
finished
);
connect
(
&
m_blameInfoProc
,
static_cast
<
void
(
QProcess
::*
)(
int
,
QProcess
::
ExitStatus
)
>
(
&
QProcess
::
finished
),
this
,
&
KateGitBlamePlugin
::
blameFinished
);
m_showProc
.
setOutputChannelMode
(
KProcess
::
SeparateChannels
);
connect
(
&
m_showProc
,
static_cast
<
void
(
QProcess
::*
)(
int
,
QProcess
::
ExitStatus
)
>
(
&
QProcess
::
finished
),
this
,
&
KateGitBlamePlugin
::
showFinished
);
}
KateGitBlamePlugin
::~
KateGitBlamePlugin
()
...
...
@@ -175,7 +190,26 @@ void KateGitBlamePlugin::viewChanged(KTextEditor::View *view)
m_blameInfoProc
.
start
();
}
void
KateGitBlamePlugin
::
finished
(
int
/*exitCode*/
,
QProcess
::
ExitStatus
/*exitStatus*/
)
void
KateGitBlamePlugin
::
showCommitInfo
(
const
QString
&
hash
,
const
QPoint
&
point
)
{
if
(
!
m_mainWindow
||
!
m_mainWindow
->
activeView
()
||
!
m_mainWindow
->
activeView
()
->
document
())
{
return
;
}
QUrl
url
=
m_mainWindow
->
activeView
()
->
document
()
->
url
();
QDir
dir
{
url
.
toLocalFile
()};
dir
.
cdUp
();
QString
shellCmd
=
QStringLiteral
(
"git show %1"
).
arg
(
hash
);
m_showProc
.
setWorkingDirectory
(
dir
.
absolutePath
());
m_showProc
.
setShellCommand
(
shellCmd
);
m_showProc
.
start
();
m_showPos
=
point
;
}
void
KateGitBlamePlugin
::
blameFinished
(
int
/*exitCode*/
,
QProcess
::
ExitStatus
/*exitStatus*/
)
{
QString
stdErr
=
QString
::
fromUtf8
(
m_blameInfoProc
.
readAllStandardError
());
const
QStringList
stdOut
=
QString
::
fromUtf8
(
m_blameInfoProc
.
readAllStandardOutput
()).
split
(
QLatin1Char
(
'\n'
));
...
...
@@ -199,6 +233,18 @@ void KateGitBlamePlugin::finished(int /*exitCode*/, QProcess::ExitStatus /*exitS
}
}
void
KateGitBlamePlugin
::
showFinished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
)
{
QString
stdErr
=
QString
::
fromUtf8
(
m_showProc
.
readAllStandardError
());
const
QString
stdOut
=
QString
::
fromUtf8
(
m_showProc
.
readAllStandardOutput
());
if
(
exitCode
!=
0
||
exitStatus
!=
QProcess
::
NormalExit
)
{
return
;
}
GitBlameTooltip
::
show
(
stdOut
,
m_mainWindow
->
activeView
());
}
bool
KateGitBlamePlugin
::
hasBlameInfo
()
const
{
return
!
m_blameInfo
.
isEmpty
();
...
...
addons/git-blame/kategitblameplugin.h
View file @
c13307f5
...
...
@@ -63,10 +63,13 @@ public:
void
readConfig
();
void
showCommitInfo
(
const
QString
&
hash
,
const
QPoint
&
point
);
private
Q_SLOTS
:
void
viewChanged
(
KTextEditor
::
View
*
view
);
void
finished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
);
void
blameFinished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
);
void
showFinished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
);
private:
void
addDocument
(
KTextEditor
::
Document
*
doc
);
...
...
@@ -74,9 +77,11 @@ private:
KTextEditor
::
MainWindow
*
m_mainWindow
;
QHash
<
KTextEditor
::
Document
*
,
GitBlameInlineNoteProvider
*>
m_inlineNoteProviders
;
KProcess
m_showProc
;
QPoint
m_showPos
;
KProcess
m_blameInfoProc
;
QVector
<
KateGitBlameInfo
>
m_blameInfo
;
KTextEditor
::
View
*
m_blameInfoView
;
KTextEditor
::
View
*
m_blameInfoView
=
nullptr
;
int
m_lineOffset
{
0
};
};
...
...
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