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
42d5ca13
Commit
42d5ca13
authored
Dec 26, 2020
by
Waqar Ahmed
Committed by
Christoph Cullmann
Dec 27, 2020
Browse files
Add Quick go-to symbol support in CTags plugin
parent
8903a672
Changes
15
Hide whitespace changes
Inline
Side-by-side
addons/kate-ctags/CMakeLists.txt
View file @
42d5ca13
...
...
@@ -22,6 +22,10 @@ target_sources(
ctagskinds.cpp
kate_ctags_view.cpp
kate_ctags_plugin.cpp
gotosymbolmodel.cpp
gotoglobalsymbolmodel.cpp
gotosymboltreeview.cpp
gotosymbolwidget.cpp
plugin.qrc
)
...
...
addons/kate-ctags/ctagskinds.cpp
View file @
42d5ca13
...
...
@@ -107,10 +107,8 @@ static CTagsExtensionMapping extensionMapping[] = {
{
"sh"
,
kindMappingSh
},
{
"SH"
,
kindMappingSh
},
{
"bsh"
,
kindMappingSh
},
{
"bash"
,
kindMappingSh
},
{
"ksh"
,
kindMappingSh
},
{
"zsh"
,
kindMappingSh
},
{
"sl"
,
kindMappingSlang
},
{
"tcl"
,
kindMappingTcl
},
{
"wish"
,
kindMappingTcl
},
{
"vim"
,
kindMappingVim
},
{
nullptr
,
nullptr
}};
static
const
CTagsKindMapping
*
findKindMapping
(
const
QString
&
extension
)
static
const
CTagsKindMapping
*
findKindMapping
(
const
char
*
p
extension
)
{
const
char
*
pextension
=
extension
.
toLocal8Bit
().
constData
();
CTagsExtensionMapping
*
pem
=
extensionMapping
;
while
(
pem
->
extension
!=
nullptr
)
{
if
(
strcmp
(
pem
->
extension
,
pextension
)
==
0
)
...
...
@@ -123,10 +121,10 @@ static const CTagsKindMapping *findKindMapping(const QString &extension)
QString
CTagsKinds
::
findKind
(
const
char
*
kindChar
,
const
QString
&
extension
)
{
if
(
kindChar
==
nullptr
)
if
(
kindChar
==
nullptr
||
extension
.
isEmpty
()
)
return
QString
();
const
CTagsKindMapping
*
kindMapping
=
findKindMapping
(
extension
);
const
CTagsKindMapping
*
kindMapping
=
findKindMapping
(
extension
.
toLocal8Bit
().
constData
()
);
if
(
kindMapping
)
{
const
CTagsKindMapping
*
pkm
=
kindMapping
;
while
(
pkm
->
verbose
!=
nullptr
)
{
...
...
@@ -138,3 +136,21 @@ QString CTagsKinds::findKind(const char *kindChar, const QString &extension)
return
QString
();
}
QString
CTagsKinds
::
findKindNoi18n
(
const
char
*
kindChar
,
const
QStringRef
&
extension
)
{
if
(
kindChar
==
nullptr
||
extension
.
isEmpty
())
return
QString
();
const
CTagsKindMapping
*
kindMapping
=
findKindMapping
(
extension
.
toLocal8Bit
().
constData
());
if
(
kindMapping
)
{
const
CTagsKindMapping
*
pkm
=
kindMapping
;
while
(
pkm
->
verbose
!=
nullptr
)
{
if
(
pkm
->
abbrev
==
*
kindChar
)
return
QString
::
fromLocal8Bit
(
pkm
->
verbose
);
++
pkm
;
}
}
return
QString
();
}
addons/kate-ctags/ctagskinds.h
View file @
42d5ca13
...
...
@@ -15,6 +15,7 @@ class CTagsKinds
{
public:
static
QString
findKind
(
const
char
*
kindChar
,
const
QString
&
extension
);
static
QString
findKindNoi18n
(
const
char
*
kindChar
,
const
QStringRef
&
extension
);
};
#endif
addons/kate-ctags/gotoglobalsymbolmodel.cpp
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gotoglobalsymbolmodel.h"
#include <QFileInfo>
#include <QIcon>
GotoGlobalSymbolModel
::
GotoGlobalSymbolModel
(
QObject
*
parent
)
:
QAbstractTableModel
(
parent
)
{
}
int
GotoGlobalSymbolModel
::
columnCount
(
const
QModelIndex
&
)
const
{
return
1
;
}
int
GotoGlobalSymbolModel
::
rowCount
(
const
QModelIndex
&
)
const
{
return
m_rows
.
size
();
}
QString
GotoGlobalSymbolModel
::
filterName
(
QString
tagName
)
const
{
// remove anon namespace
int
__anonIdx
=
tagName
.
indexOf
(
QStringLiteral
(
"__anon"
));
if
(
__anonIdx
!=
-
1
)
{
int
scopeOpIdx
=
tagName
.
indexOf
(
QStringLiteral
(
"::"
),
__anonIdx
)
+
2
;
tagName
.
remove
(
__anonIdx
,
scopeOpIdx
-
__anonIdx
);
}
return
tagName
;
}
QVariant
GotoGlobalSymbolModel
::
data
(
const
QModelIndex
&
index
,
int
role
)
const
{
if
(
!
index
.
isValid
())
{
return
QVariant
();
}
static
const
QIcon
defIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-block"
));
static
const
QIcon
funcIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-function"
));
static
const
QIcon
varIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-variable"
));
const
Tags
::
TagEntry
&
row
=
m_rows
.
at
(
index
.
row
());
if
(
role
==
Qt
::
DisplayRole
)
{
if
(
index
.
column
()
==
0
)
return
QString
(
filterName
(
row
.
tag
)
+
QStringLiteral
(
" <sub style=
\"
font-size: 16px;color: gray;
\"
>"
)
+
QFileInfo
(
row
.
file
).
fileName
()
+
QStringLiteral
(
"</sub>"
));
}
else
if
(
role
==
Qt
::
UserRole
)
{
return
row
.
tag
;
}
else
if
(
role
==
Qt
::
DecorationRole
)
{
if
(
row
.
type
==
QLatin1String
(
"function"
)
||
row
.
type
==
QLatin1String
(
"member"
))
{
return
funcIcon
;
}
else
if
(
row
.
type
.
startsWith
(
QLatin1String
(
"var"
)))
{
return
varIcon
;
}
else
{
return
defIcon
;
}
}
else
if
(
role
==
Pattern
)
{
return
row
.
pattern
;
}
else
if
(
role
==
FileUrl
)
{
return
row
.
file
;
}
return
QVariant
();
}
addons/kate-ctags/gotoglobalsymbolmodel.h
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef GOTOGLOBALSYMBOLMODEL_H
#define GOTOGLOBALSYMBOLMODEL_H
#include "tags.h"
#include <QAbstractTableModel>
#include <QPair>
class
GotoGlobalSymbolModel
:
public
QAbstractTableModel
{
Q_OBJECT
public:
struct
GSymbolItem
{
QString
name
;
QString
fileName
;
QString
fileUrl
;
QString
pattern
;
QString
type
;
};
enum
Roles
{
Name
=
Qt
::
UserRole
,
Pattern
,
FileUrl
};
explicit
GotoGlobalSymbolModel
(
QObject
*
parent
=
nullptr
);
int
columnCount
(
const
QModelIndex
&
parent
=
QModelIndex
())
const
override
;
int
rowCount
(
const
QModelIndex
&
parent
=
QModelIndex
())
const
override
;
QVariant
data
(
const
QModelIndex
&
index
,
int
role
)
const
override
;
/**
* @brief removes useless symbols like anon namespace etc for better UI
*/
QString
filterName
(
QString
tagName
)
const
;
void
setSymbolsData
(
Tags
::
TagList
rows
)
{
beginResetModel
();
m_rows
=
std
::
move
(
rows
);
endResetModel
();
}
private:
Tags
::
TagList
m_rows
;
};
#endif // GOTOGLOBALSYMBOLMODEL_H
addons/kate-ctags/gotosymbolmodel.cpp
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gotosymbolmodel.h"
#include <QProcess>
#include <QDebug>
#include <KLocalizedString>
GotoSymbolModel
::
GotoSymbolModel
(
QObject
*
parent
)
:
QAbstractTableModel
(
parent
)
{
}
int
GotoSymbolModel
::
columnCount
(
const
QModelIndex
&
)
const
{
return
1
;
}
int
GotoSymbolModel
::
rowCount
(
const
QModelIndex
&
)
const
{
return
m_rows
.
count
();
}
QVariant
GotoSymbolModel
::
data
(
const
QModelIndex
&
index
,
int
role
)
const
{
if
(
!
index
.
isValid
())
{
return
{};
}
const
auto
&
row
=
m_rows
.
at
(
index
.
row
());
if
(
role
==
Qt
::
DisplayRole
)
{
if
(
index
.
column
()
==
0
)
return
row
.
name
;
}
else
if
(
role
==
Qt
::
DecorationRole
)
{
if
(
index
.
column
()
==
0
)
return
row
.
icon
;
}
else
if
(
role
==
Qt
::
UserRole
)
{
return
row
.
line
;
}
return
QVariant
();
}
void
GotoSymbolModel
::
refresh
(
const
QString
&
filePath
)
{
static
const
QIcon
nsIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-block"
));
static
const
QIcon
classIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-class"
));
static
const
QIcon
funcIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-function"
));
static
const
QIcon
varIcon
=
QIcon
::
fromTheme
(
QStringLiteral
(
"code-variable"
));
static
const
QIcon
defIcon
=
nsIcon
;
beginResetModel
();
m_rows
.
clear
();
endResetModel
();
QProcess
p
;
p
.
start
(
QStringLiteral
(
"ctags"
),
{
QStringLiteral
(
"-x"
),
QStringLiteral
(
"--_xformat=%{name}%{signature}
\t
%{kind}
\t
%{line}"
),
filePath
});
QByteArray
out
;
if
(
p
.
waitForFinished
())
{
out
=
p
.
readAllStandardOutput
();
}
else
{
qWarning
()
<<
"Ctags failed"
;
beginResetModel
();
m_rows
.
append
(
SymbolItem
{
i18n
(
"CTags executable not found."
),
-
1
,
QIcon
()});
endResetModel
();
return
;
}
QVector
<
SymbolItem
>
symItems
;
const
auto
tags
=
out
.
split
(
'\n'
);
symItems
.
reserve
(
tags
.
size
());
for
(
const
auto
&
tag
:
tags
)
{
const
auto
items
=
tag
.
split
(
'\t'
);
if
(
items
.
isEmpty
()
||
items
.
count
()
<
3
){
continue
;
}
SymbolItem
item
;
item
.
name
=
QLatin1String
(
items
.
at
(
0
));
// this happens in markdown names for some reason
if
(
item
.
name
.
endsWith
(
QLatin1Char
(
'-'
)))
{
item
.
name
.
chop
(
1
);
}
switch
(
items
.
at
(
1
).
at
(
0
))
{
case
'f'
:
item
.
icon
=
funcIcon
;
break
;
case
'm'
:
if
(
items
.
at
(
1
)
==
"method"
)
item
.
icon
=
funcIcon
;
else
item
.
icon
=
defIcon
;
break
;
case
'g'
:
if
(
items
.
at
(
1
)
==
"getter"
)
item
.
icon
=
funcIcon
;
else
item
.
icon
=
defIcon
;
break
;
case
'c'
:
case
's'
:
if
(
items
.
at
(
1
)
==
"class"
||
items
.
at
(
1
)
==
"struct"
)
item
.
icon
=
classIcon
;
else
item
.
icon
=
defIcon
;
break
;
case
'n'
:
if
(
items
.
at
(
1
)
==
"namespace"
)
item
.
icon
=
nsIcon
;
break
;
case
'v'
:
item
.
icon
=
varIcon
;
break
;
default:
item
.
icon
=
defIcon
;
break
;
}
item
.
line
=
items
.
at
(
2
).
toInt
();
symItems
.
append
(
item
);
}
beginResetModel
();
if
(
!
symItems
.
isEmpty
())
{
m_rows
=
std
::
move
(
symItems
);
}
else
{
m_rows
.
append
(
SymbolItem
{
i18n
(
"CTags was unable to parse this file."
),
-
1
,
QIcon
()});
}
endResetModel
();
}
addons/kate-ctags/gotosymbolmodel.h
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef GOTOSYMBOLMODEL_H
#define GOTOSYMBOLMODEL_H
#include <QString>
#include <QVector>
#include <QAbstractTableModel>
#include <QIcon>
struct
SymbolItem
{
QString
name
;
int
line
;
QIcon
icon
;
};
class
GotoSymbolModel
:
public
QAbstractTableModel
{
Q_OBJECT
public:
explicit
GotoSymbolModel
(
QObject
*
parent
=
nullptr
);
int
columnCount
(
const
QModelIndex
&
parent
=
QModelIndex
())
const
override
;
int
rowCount
(
const
QModelIndex
&
parent
=
QModelIndex
())
const
override
;
QVariant
data
(
const
QModelIndex
&
index
,
int
role
)
const
override
;
void
refresh
(
const
QString
&
filePath
);
private:
QVector
<
SymbolItem
>
m_rows
;
};
#endif // GOTOSYMBOLMODEL_H
addons/kate-ctags/gotosymboltreeview.cpp
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gotosymboltreeview.h"
#include <KTextEditor/Cursor>
#include <KTextEditor/MainWindow>
#include <KTextEditor/View>
#include <QHeaderView>
GotoSymbolTreeView
::
GotoSymbolTreeView
(
KTextEditor
::
MainWindow
*
mainWindow
,
QWidget
*
parent
)
:
QTreeView
(
parent
),
m_mainWindow
(
mainWindow
)
{
setSelectionBehavior
(
QAbstractItemView
::
SelectRows
);
setSelectionMode
(
QAbstractItemView
::
SingleSelection
);
setTextElideMode
(
Qt
::
ElideRight
);
setHorizontalScrollBarPolicy
(
Qt
::
ScrollBarAlwaysOff
);
setHeaderHidden
(
true
);
setRootIsDecorated
(
false
);
}
int
GotoSymbolTreeView
::
sizeHintWidth
()
const
{
return
sizeHintForColumn
(
0
);
}
void
GotoSymbolTreeView
::
currentChanged
(
const
QModelIndex
&
current
,
const
QModelIndex
&
previous
)
{
if
(
globalMode
)
return
QTreeView
::
currentChanged
(
current
,
previous
);
int
line
=
current
.
data
(
Qt
::
UserRole
).
toInt
();
KTextEditor
::
Cursor
c
(
--
line
,
0
);
if
(
c
.
isValid
())
{
auto
view
=
m_mainWindow
->
activeView
();
if
(
view
)
{
view
->
setCursorPosition
(
c
);
}
}
return
QTreeView
::
currentChanged
(
current
,
previous
);
}
addons/kate-ctags/gotosymboltreeview.h
0 → 100644
View file @
42d5ca13
#ifndef GOTOSYMBOLTREEVIEW_H
#define GOTOSYMBOLTREEVIEW_H
/*
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <QTreeView>
namespace
KTextEditor
{
class
MainWindow
;
}
class
GotoSymbolTreeView
:
public
QTreeView
{
Q_OBJECT
public:
GotoSymbolTreeView
(
KTextEditor
::
MainWindow
*
mainWindow
,
QWidget
*
parent
=
nullptr
);
int
sizeHintWidth
()
const
;
void
setGlobalMode
(
bool
value
)
{
globalMode
=
value
;
}
protected:
void
currentChanged
(
const
QModelIndex
&
current
,
const
QModelIndex
&
previous
)
override
;
private:
KTextEditor
::
MainWindow
*
m_mainWindow
;
bool
globalMode
=
false
;
};
#endif // GOTOSYMBOLTREEVIEW_H
addons/kate-ctags/gotosymbolwidget.cpp
0 → 100644
View file @
42d5ca13
/*
SPDX-FileCopyrightText: 2014-2019 Dominik Haumann <dhaumann@kde.org>
SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gotosymbolwidget.h"
#include "gotosymboltreeview.h"
#include "gotosymbolmodel.h"
#include "gotoglobalsymbolmodel.h"
#include "tags.h"
#include "kate_ctags_view.h"
#include <QLineEdit>
#include <QVBoxLayout>
#include <QKeyEvent>
#include <QScrollBar>
#include <QSortFilterProxyModel>
#include <QCoreApplication>
#include <QStyledItemDelegate>
#include <QTextDocument>
#include <QPainter>
#include <QPropertyAnimation>
#include <KTextEditor/MainWindow>
#include <KTextEditor/View>
class
QuickOpenFilterProxyModel
:
public
QSortFilterProxyModel
{
public:
QuickOpenFilterProxyModel
(
QObject
*
parent
=
nullptr
)
:
QSortFilterProxyModel
(
parent
)
{}
bool
filterAcceptsRow
(
int
sourceRow
,
const
QModelIndex
&
sourceParent
)
const
override
{
const
QString
fileName
=
sourceModel
()
->
index
(
sourceRow
,
0
,
sourceParent
).
data
().
toString
();
for
(
const
QString
&
str
:
m_filterStrings
)
{
if
(
!
fileName
.
contains
(
str
,
Qt
::
CaseInsensitive
))
{
return
false
;
}
}
return
true
;
}
QStringList
filterStrings
()
const
{
return
m_filterStrings
;
}
public
Q_SLOTS
:
void
setFilterText
(
const
QString
&
text
)
{
m_filterStrings
=
text
.
split
(
QLatin1Char
(
' '
),
Qt
::
SkipEmptyParts
);
invalidateFilter
();
}
private:
QStringList
m_filterStrings
;
};
class
GotoStyleDelegate
:
public
QStyledItemDelegate
{
public:
GotoStyleDelegate
(
QObject
*
parent
=
nullptr
)
:
QStyledItemDelegate
(
parent
)
{}
void
paint
(
QPainter
*
painter
,
const
QStyleOptionViewItem
&
option
,
const
QModelIndex
&
index
)
const
override
{
QStyleOptionViewItem
options
=
option
;
initStyleOption
(
&
options
,
index
);
QTextDocument
doc
;
QString
str
=
index
.
data
().
toString
();
for
(
const
auto
&
string
:
m_filterStrings
)
{
const
QRegularExpression
re
(
QStringLiteral
(
"("
)
+
QRegularExpression
::
escape
(
string
)
+
QStringLiteral
(
")"
),
QRegularExpression
::
CaseInsensitiveOption
);
str
.
replace
(
re
,
QStringLiteral
(
"<b>
\\
1</b>"
));
}
doc
.
setHtml
(
str
);
doc
.
setDocumentMargin
(
2
);
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
());
}
options
.
text
=
QString
();
// clear old text
options
.
widget
->
style
()
->
drawControl
(
QStyle
::
CE_ItemViewItem
,
&
options
,
painter
,
options
.
widget
);
// draw text
painter
->
translate
(
option
.
rect
.
x
(),
option
.
rect
.
y
());
if
(
index
.
column
()
==
0
)
{
painter
->
translate
(
25
,
0
);
}
doc
.
drawContents
(
painter
);
painter
->
restore
();
}
public
Q_SLOTS
:
void
setFilterStrings
(
const
QString
&
text
)
{
m_filterStrings
=
text
.
split
(
QLatin1Char
(
' '
),
Qt
::
SkipEmptyParts
);
}
private:
QStringList
m_filterStrings
;
};
GotoSymbolWidget
::
GotoSymbolWidget
(
KTextEditor
::
MainWindow
*
mainWindow
,
KateCTagsView
*
pluginView
,
QWidget
*
widget
)
:
QWidget
(
widget
),
ctagsPluginView
(
pluginView
),
m_mainWindow
(
mainWindow
),
oldPos
(
-
1
,
-
1
)
{
setWindowFlags
(
Qt
::
Popup
|
Qt
::
FramelessWindowHint
);
mode
=
Local
;
m_treeView
=
new
GotoSymbolTreeView
(
mainWindow
,
this
);
m_styleDelegate
=
new
GotoStyleDelegate
(
this
);
m_treeView
->
setItemDelegate
(
m_styleDelegate
);
m_lineEdit
=
new
QLineEdit
(
this
);
setFocusProxy
(
m_lineEdit
);
m_proxyModel
=
new
QuickOpenFilterProxyModel
(
this
);
m_proxyModel
->
setSortRole
(
Qt
::
DisplayRole
);
m_proxyModel
->
setSortCaseSensitivity
(
Qt
::
CaseInsensitive
);
m_proxyModel
->
setFilterRole
(
Qt
::
DisplayRole
);
m_proxyModel
->
setFilterCaseSensitivity
(
Qt
::
CaseInsensitive
);
m_proxyModel
->
setFilterKeyColumn
(
0
);
m_symbolsModel
=
new
GotoSymbolModel
(
this
);
m_globalSymbolsModel
=
new
GotoGlobalSymbolModel
(
this
);
m_proxyModel
->
setSourceModel
(
m_symbolsModel
);
m_treeView
->
setModel
(
m_proxyModel
);
connect
(
m_lineEdit
,
&
QLineEdit
::
textChanged
,
m_proxyModel
,
&
QuickOpenFilterProxyModel
::
setFilterText
);
connect
(
m_lineEdit
,
&
QLineEdit
::
textChanged
,
m_styleDelegate
,
&
GotoStyleDelegate
::
setFilterStrings
);
connect
(
m_lineEdit
,
&
QLineEdit
::
textChanged
,
this
,
[
this
](){
m_treeView
->
viewport
()
->
update
();
});
connect
(
m_lineEdit
,
&
QLineEdit
::
textChanged
,
this
,
&
GotoSymbolWidget
::
loadGlobalSymbols
);