File indexing completed on 2024-05-05 05:51:38

0001 /* Description : Kate CTags plugin
0002  *
0003  * SPDX-FileCopyrightText: 2008-2011 Kare Sars <kare.sars@iki.fi>
0004  *
0005  * This program is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2.1 of the License, or (at your option) version 3, or any
0009  * later version accepted by the membership of KDE e.V. (or its
0010  * successor approved by the membership of KDE e.V.), which shall
0011  * act as a proxy defined in Section 6 of version 3 of the license.
0012  *
0013  * This program is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU General Public
0019  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
0020  */
0021 
0022 #include "kate_ctags_view.h"
0023 #include "kate_ctags_debug.h"
0024 #include "kate_ctags_plugin.h"
0025 
0026 #include "hostprocess.h"
0027 
0028 #include <QFileDialog>
0029 #include <QFileInfo>
0030 #include <QKeyEvent>
0031 
0032 #include <KActionCollection>
0033 #include <KConfigGroup>
0034 #include <KXMLGUIFactory>
0035 #include <QMenu>
0036 
0037 #include <KLocalizedString>
0038 #include <KStringHandler>
0039 #include <QDialogButtonBox>
0040 #include <QStandardPaths>
0041 
0042 #include <ktexteditor_utils.h>
0043 
0044 /******************************************************************/
0045 KateCTagsView::KateCTagsView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mainWin)
0046     : QObject(mainWin)
0047     , m_proc(nullptr)
0048 {
0049     KXMLGUIClient::setComponentName(QStringLiteral("katectags"), i18n("CTags"));
0050     setXMLFile(QStringLiteral("ui.rc"));
0051 
0052     m_toolView = mainWin->createToolView(plugin,
0053                                          QStringLiteral("kate_plugin_katectagsplugin"),
0054                                          KTextEditor::MainWindow::Bottom,
0055                                          QIcon::fromTheme(QStringLiteral("application-x-ms-dos-executable")),
0056                                          i18n("CTags"));
0057     m_mWin = mainWin;
0058 
0059     QAction *back = actionCollection()->addAction(QStringLiteral("ctags_return_step"));
0060     back->setText(i18n("Jump back one step"));
0061     connect(back, &QAction::triggered, this, &KateCTagsView::stepBack);
0062 
0063     QAction *decl = actionCollection()->addAction(QStringLiteral("ctags_lookup_current_as_declaration"));
0064     decl->setText(i18n("Go to Declaration"));
0065     connect(decl, &QAction::triggered, this, &KateCTagsView::gotoDeclaration);
0066 
0067     QAction *defin = actionCollection()->addAction(QStringLiteral("ctags_lookup_current_as_definition"));
0068     defin->setText(i18n("Go to Definition"));
0069     connect(defin, &QAction::triggered, this, &KateCTagsView::gotoDefinition);
0070 
0071     QAction *lookup = actionCollection()->addAction(QStringLiteral("ctags_lookup_current"));
0072     lookup->setText(i18n("Lookup Current Text"));
0073     connect(lookup, &QAction::triggered, this, &KateCTagsView::lookupTag);
0074 
0075     QAction *updateDB = actionCollection()->addAction(QStringLiteral("ctags_update_global_db"));
0076     updateDB->setText(i18n("Configure ..."));
0077     connect(updateDB, &QAction::triggered, this, [this, plugin](bool) {
0078         if (m_mWin) {
0079             KateCTagsPlugin *p = static_cast<KateCTagsPlugin *>(plugin);
0080             QDialog *confWin = new QDialog(m_mWin->window());
0081             confWin->setAttribute(Qt::WA_DeleteOnClose);
0082             auto confPage = p->configPage(0, confWin);
0083             auto controls = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, confWin);
0084             connect(confWin, &QDialog::accepted, confPage, &KTextEditor::ConfigPage::apply);
0085             connect(controls, &QDialogButtonBox::accepted, confWin, &QDialog::accept);
0086             connect(controls, &QDialogButtonBox::rejected, confWin, &QDialog::reject);
0087             auto layout = new QVBoxLayout(confWin);
0088             layout->addWidget(confPage);
0089             layout->addWidget(controls);
0090             confWin->setLayout(layout);
0091             confWin->setWindowTitle(i18nc("@title:window", "Configure CTags Plugin"));
0092             confWin->setWindowIcon(confPage->icon());
0093             confWin->show();
0094             confWin->exec();
0095         }
0096     });
0097 
0098     // popup menu
0099     m_menu = new KActionMenu(i18n("CTags"), this);
0100     actionCollection()->addAction(QStringLiteral("popup_ctags"), m_menu);
0101 
0102     m_gotoDec = m_menu->menu()->addAction(i18n("Go to Declaration: %1", QString()), this, &KateCTagsView::gotoDeclaration);
0103     m_gotoDef = m_menu->menu()->addAction(i18n("Go to Definition: %1", QString()), this, &KateCTagsView::gotoDefinition);
0104     m_lookup = m_menu->menu()->addAction(i18n("Lookup: %1", QString()), this, &KateCTagsView::lookupTag);
0105 
0106     connect(m_menu->menu(), &QMenu::aboutToShow, this, &KateCTagsView::aboutToShow);
0107 
0108     QWidget *ctagsWidget = new QWidget(m_toolView.data());
0109     m_ctagsUi.setupUi(ctagsWidget);
0110     m_ctagsUi.cmdEdit->setText(DEFAULT_CTAGS_CMD);
0111 
0112     m_ctagsUi.addButton->setToolTip(i18n("Add a directory to index."));
0113     m_ctagsUi.addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0114 
0115     m_ctagsUi.delButton->setToolTip(i18n("Remove a directory."));
0116     m_ctagsUi.delButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0117 
0118     m_ctagsUi.updateButton->setToolTip(i18n("(Re-)generate the session specific CTags database."));
0119     m_ctagsUi.updateButton->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0120 
0121     m_ctagsUi.updateButton2->setToolTip(i18n("(Re-)generate the session specific CTags database."));
0122     m_ctagsUi.updateButton2->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0123 
0124     m_ctagsUi.resetCMD->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0125 
0126     m_ctagsUi.tagsFile->setToolTip(i18n("Select new or existing database file."));
0127     m_ctagsUi.tagsFile->setMode(KFile::File);
0128 
0129     connect(m_ctagsUi.resetCMD, &QToolButton::clicked, this, &KateCTagsView::resetCMD);
0130     connect(m_ctagsUi.addButton, &QPushButton::clicked, this, &KateCTagsView::addTagTarget);
0131     connect(m_ctagsUi.delButton, &QPushButton::clicked, this, &KateCTagsView::delTagTarget);
0132     connect(m_ctagsUi.updateButton, &QPushButton::clicked, this, &KateCTagsView::updateSessionDB);
0133     connect(m_ctagsUi.updateButton2, &QPushButton::clicked, this, &KateCTagsView::updateSessionDB);
0134     connect(&m_proc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &KateCTagsView::updateDone);
0135     connect(&m_proc, &QProcess::readyReadStandardError, this, [this]() {
0136         QString error = QString::fromLocal8Bit(m_proc.readAllStandardError());
0137         Utils::showMessage(error, QIcon(), i18n("CTags"), MessageType::Error);
0138     });
0139 
0140     m_gotoSymbWidget.reset(new GotoSymbolWidget(mainWin, this));
0141     auto openLocal = actionCollection()->addAction(QStringLiteral("open_local_gts"));
0142     openLocal->setText(i18n("Go To Local Symbol"));
0143     actionCollection()->setDefaultShortcut(openLocal, Qt::CTRL | Qt::ALT | Qt::Key_P);
0144     connect(openLocal, &QAction::triggered, this, &KateCTagsView::showSymbols);
0145 
0146     auto openGlobal = actionCollection()->addAction(QStringLiteral("open_global_gts"));
0147     openGlobal->setText(i18n("Go To Global Symbol"));
0148     actionCollection()->setDefaultShortcut(openGlobal, Qt::CTRL | Qt::SHIFT | Qt::Key_P);
0149     connect(openGlobal, &QAction::triggered, this, &KateCTagsView::showGlobalSymbols);
0150 
0151     connect(m_ctagsUi.inputEdit, &QLineEdit::textChanged, this, &KateCTagsView::startEditTmr);
0152 
0153     m_editTimer.setSingleShot(true);
0154     connect(&m_editTimer, &QTimer::timeout, this, &KateCTagsView::editLookUp);
0155 
0156     connect(m_ctagsUi.tagTreeWidget, &QTreeWidget::itemActivated, this, &KateCTagsView::tagHitClicked);
0157 
0158     connect(m_mWin, &KTextEditor::MainWindow::unhandledShortcutOverride, this, &KateCTagsView::handleEsc);
0159 
0160     m_toolView->layout()->addWidget(ctagsWidget);
0161     m_toolView->installEventFilter(this);
0162 
0163     m_mWin->guiFactory()->addClient(this);
0164 
0165     m_commonDB = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/katectags/common_db");
0166 }
0167 
0168 /******************************************************************/
0169 KateCTagsView::~KateCTagsView()
0170 {
0171     if (m_mWin && m_mWin->guiFactory()) {
0172         m_mWin->guiFactory()->removeClient(this);
0173     }
0174 
0175     if (m_toolView) {
0176         delete m_toolView;
0177     }
0178 }
0179 
0180 /******************************************************************/
0181 void KateCTagsView::aboutToShow()
0182 {
0183     QString currWord = currentWord();
0184     if (currWord.isEmpty()) {
0185         return;
0186     }
0187 
0188     if (Tags::hasTag(m_commonDB, currWord) || Tags::hasTag(m_ctagsUi.tagsFile->text(), currWord)) {
0189         QString squeezed = KStringHandler::csqueeze(currWord, 30);
0190 
0191         m_gotoDec->setText(i18n("Go to Declaration: %1", squeezed));
0192         m_gotoDef->setText(i18n("Go to Definition: %1", squeezed));
0193         m_lookup->setText(i18n("Lookup: %1", squeezed));
0194     }
0195 }
0196 
0197 /******************************************************************/
0198 void KateCTagsView::readSessionConfig(const KConfigGroup &cg)
0199 {
0200     m_ctagsUi.cmdEdit->setText(cg.readEntry("TagsGenCMD", DEFAULT_CTAGS_CMD));
0201 
0202     int numEntries = cg.readEntry("SessionNumTargets", 0);
0203     QString nr;
0204     QString target;
0205     for (int i = 0; i < numEntries; i++) {
0206         nr = QStringLiteral("%1").arg(i, 3);
0207         target = cg.readEntry(QStringLiteral("SessionTarget_%1").arg(nr), QString());
0208         if (!listContains(target)) {
0209             new QListWidgetItem(target, m_ctagsUi.targetList);
0210         }
0211     }
0212 
0213     QString sessionDB = cg.readEntry("SessionDatabase", QString());
0214     m_ctagsUi.tagsFile->setText(sessionDB);
0215 }
0216 
0217 /******************************************************************/
0218 void KateCTagsView::writeSessionConfig(KConfigGroup &cg)
0219 {
0220     cg.writeEntry("TagsGenCMD", m_ctagsUi.cmdEdit->text());
0221     cg.writeEntry("SessionNumTargets", m_ctagsUi.targetList->count());
0222 
0223     QString nr;
0224     for (int i = 0; i < m_ctagsUi.targetList->count(); i++) {
0225         nr = QStringLiteral("%1").arg(i, 3);
0226         cg.writeEntry(QStringLiteral("SessionTarget_%1").arg(nr), m_ctagsUi.targetList->item(i)->text());
0227     }
0228 
0229     cg.writeEntry("SessionDatabase", m_ctagsUi.tagsFile->text());
0230 
0231     cg.sync();
0232 }
0233 
0234 /******************************************************************/
0235 void KateCTagsView::stepBack()
0236 {
0237     if (m_jumpStack.isEmpty()) {
0238         return;
0239     }
0240 
0241     TagJump back;
0242     back = m_jumpStack.pop();
0243 
0244     m_mWin->openUrl(back.url);
0245     if (m_mWin->activeView()) {
0246         m_mWin->activeView()->setCursorPosition(back.cursor);
0247         m_mWin->activeView()->setFocus();
0248     }
0249 }
0250 
0251 /******************************************************************/
0252 void KateCTagsView::lookupTag()
0253 {
0254     QString currWord = currentWord();
0255     if (currWord.isEmpty()) {
0256         return;
0257     }
0258 
0259     setNewLookupText(currWord);
0260     Tags::TagList list = Tags::getExactMatches(m_ctagsUi.tagsFile->text(), currWord);
0261     if (list.empty()) {
0262         list = Tags::getExactMatches(m_commonDB, currWord);
0263     }
0264     displayHits(list);
0265 
0266     // activate the hits tab
0267     m_ctagsUi.tabWidget->setCurrentIndex(0);
0268     m_mWin->showToolView(m_toolView);
0269 }
0270 
0271 /******************************************************************/
0272 void KateCTagsView::editLookUp()
0273 {
0274     Tags::TagList list = Tags::getPartialMatches(m_ctagsUi.tagsFile->text(), m_ctagsUi.inputEdit->text());
0275     if (list.empty()) {
0276         list = Tags::getPartialMatches(m_commonDB, m_ctagsUi.inputEdit->text());
0277     }
0278     displayHits(list);
0279 }
0280 
0281 /******************************************************************/
0282 void KateCTagsView::gotoDefinition()
0283 {
0284     QString currWord = currentWord();
0285     if (currWord.isEmpty()) {
0286         return;
0287     }
0288 
0289     QStringList types;
0290     types << QStringLiteral("S") << QStringLiteral("d") << QStringLiteral("f") << QStringLiteral("t") << QStringLiteral("v");
0291     Tags::TagList list = Tags::getMatches(m_ctagsUi.tagsFile->text(), currWord, false, types);
0292     if (list.isEmpty()) {
0293         gotoDeclaration();
0294     } else {
0295         gotoResults(currWord, list);
0296     }
0297 }
0298 
0299 /******************************************************************/
0300 void KateCTagsView::gotoDeclaration()
0301 {
0302     QString currWord = currentWord();
0303     if (currWord.isEmpty()) {
0304         return;
0305     }
0306 
0307     QStringList types;
0308     types << QStringLiteral("L") << QStringLiteral("c") << QStringLiteral("e") << QStringLiteral("g") << QStringLiteral("m") << QStringLiteral("n")
0309           << QStringLiteral("p") << QStringLiteral("s") << QStringLiteral("u") << QStringLiteral("x");
0310     Tags::TagList list = Tags::getMatches(m_ctagsUi.tagsFile->text(), currWord, false, types);
0311     gotoResults(currWord, list);
0312 }
0313 
0314 /******************************************************************/
0315 void KateCTagsView::gotoResults(const QString &word, const Tags::TagList &list)
0316 {
0317     // qCDebug(KTECTAGS) << "found" << list.count() << word << types;
0318     setNewLookupText(word);
0319 
0320     if (list.count() < 1) {
0321         m_ctagsUi.tagTreeWidget->clear();
0322         new QTreeWidgetItem(m_ctagsUi.tagTreeWidget, QStringList(i18n("No hits found")));
0323         m_ctagsUi.tabWidget->setCurrentIndex(0);
0324         m_mWin->showToolView(m_toolView);
0325         return;
0326     }
0327 
0328     displayHits(list);
0329 
0330     if (list.count() == 1) {
0331         Tags::TagEntry tag = list.first();
0332         jumpToTag(tag.file, tag.pattern, word);
0333     } else {
0334         Tags::TagEntry tag = list.first();
0335         jumpToTag(tag.file, tag.pattern, word);
0336         m_ctagsUi.tabWidget->setCurrentIndex(0);
0337         m_mWin->showToolView(m_toolView);
0338     }
0339 }
0340 
0341 /******************************************************************/
0342 void KateCTagsView::setNewLookupText(const QString &newString)
0343 {
0344     m_ctagsUi.inputEdit->blockSignals(true);
0345     m_ctagsUi.inputEdit->setText(newString);
0346     m_ctagsUi.inputEdit->blockSignals(false);
0347 }
0348 
0349 /******************************************************************/
0350 void KateCTagsView::displayHits(const Tags::TagList &list)
0351 {
0352     m_ctagsUi.tagTreeWidget->clear();
0353     if (list.isEmpty()) {
0354         new QTreeWidgetItem(m_ctagsUi.tagTreeWidget, QStringList(i18n("No hits found")));
0355         return;
0356     }
0357     m_ctagsUi.tagTreeWidget->setSortingEnabled(false);
0358 
0359     for (const auto &tag : list) {
0360         QTreeWidgetItem *item = new QTreeWidgetItem(m_ctagsUi.tagTreeWidget);
0361         item->setText(0, tag.tag);
0362         item->setText(1, tag.type);
0363         item->setText(2, tag.file);
0364         item->setData(0, Qt::UserRole, tag.pattern);
0365 
0366         QString pattern = tag.pattern;
0367         pattern.replace(QStringLiteral("\\/"), QStringLiteral("/"));
0368         pattern = pattern.mid(2, pattern.length() - 4);
0369         pattern = pattern.trimmed();
0370 
0371         item->setData(0, Qt::ToolTipRole, pattern);
0372         item->setData(1, Qt::ToolTipRole, pattern);
0373         item->setData(2, Qt::ToolTipRole, pattern);
0374     }
0375     m_ctagsUi.tagTreeWidget->setSortingEnabled(true);
0376 }
0377 
0378 /******************************************************************/
0379 void KateCTagsView::tagHitClicked(QTreeWidgetItem *item)
0380 {
0381     // get stuff
0382     const QString file = item->data(2, Qt::DisplayRole).toString();
0383     const QString pattern = item->data(0, Qt::UserRole).toString();
0384     const QString word = item->data(0, Qt::DisplayRole).toString();
0385 
0386     jumpToTag(file, pattern, word);
0387 }
0388 
0389 /******************************************************************/
0390 QString KateCTagsView::currentWord()
0391 {
0392     KTextEditor::View *kv = m_mWin->activeView();
0393     if (!kv) {
0394         qCDebug(KTECTAGS) << "no KTextEditor::View";
0395         return QString();
0396     }
0397 
0398     if (kv->selection() && kv->selectionRange().onSingleLine()) {
0399         return kv->selectionText();
0400     }
0401 
0402     if (!kv->cursorPosition().isValid()) {
0403         qCDebug(KTECTAGS) << "cursor not valid!";
0404         return QString();
0405     }
0406 
0407     int line = kv->cursorPosition().line();
0408     int col = kv->cursorPosition().column();
0409     bool includeColon = m_ctagsUi.cmdEdit->text().contains(QLatin1String("--extra=+q"));
0410 
0411     QString linestr = kv->document()->line(line);
0412 
0413     int startPos = qMax(qMin(col, linestr.length() - 1), 0);
0414     int endPos = startPos;
0415     while (startPos >= 0
0416            && (linestr[startPos].isLetterOrNumber() || (linestr[startPos] == QLatin1Char(':') && includeColon) || linestr[startPos] == QLatin1Char('_')
0417                || linestr[startPos] == QLatin1Char('~'))) {
0418         startPos--;
0419     }
0420     while (endPos < linestr.length()
0421            && (linestr[endPos].isLetterOrNumber() || (linestr[endPos] == QLatin1Char(':') && includeColon) || linestr[endPos] == QLatin1Char('_'))) {
0422         endPos++;
0423     }
0424     if (startPos == endPos) {
0425         qCDebug(KTECTAGS) << "no word found!";
0426         return QString();
0427     }
0428 
0429     linestr = linestr.mid(startPos + 1, endPos - startPos - 1);
0430 
0431     while (linestr.endsWith(QLatin1Char(':'))) {
0432         linestr.remove(linestr.size() - 1, 1);
0433     }
0434 
0435     while (linestr.startsWith(QLatin1Char(':'))) {
0436         linestr.remove(0, 1);
0437     }
0438 
0439     // qCDebug(KTECTAGS) << linestr;
0440     return linestr;
0441 }
0442 
0443 /******************************************************************/
0444 void KateCTagsView::jumpToTag(const QString &file, const QString &pattern, const QString &word)
0445 {
0446     if (pattern.isEmpty()) {
0447         return;
0448     }
0449 
0450     // generate a regexp from the pattern
0451     // ctags interestingly escapes "/", but apparently nothing else. lets revert that
0452     QString unescaped = pattern;
0453     unescaped.replace(QStringLiteral("\\/"), QStringLiteral("/"));
0454 
0455     // most of the time, the ctags pattern has the form /^foo$/
0456     // but this isn't true for some macro definitions
0457     // where the form is only /^foo/
0458     // I have no idea if this is a ctags bug or not, but we have to deal with it
0459 
0460     QString reduced;
0461     QString escaped;
0462     QString re_string;
0463 
0464     if (unescaped.endsWith(QLatin1String("$/"))) {
0465         reduced = unescaped.mid(2, unescaped.length() - 4);
0466         escaped = QRegularExpression::escape(reduced);
0467         re_string = QStringLiteral("^%1$").arg(escaped);
0468     } else {
0469         reduced = unescaped.mid(2, unescaped.length() - 3);
0470         escaped = QRegularExpression::escape(reduced);
0471         re_string = QStringLiteral("^%1").arg(escaped);
0472     }
0473 
0474     QRegularExpression re(re_string);
0475 
0476     // save current location
0477     TagJump from;
0478     if (auto v = m_mWin->activeView()) {
0479         from.url = v->document()->url();
0480         from.cursor = v->cursorPosition();
0481     }
0482     m_jumpStack.push(from);
0483 
0484     // open/activate the new file
0485     QFileInfo fInfo(file);
0486     // qCDebug(KTECTAGS) << pattern << file << fInfo.absoluteFilePath();
0487     m_mWin->openUrl(QUrl::fromLocalFile(fInfo.absoluteFilePath()));
0488 
0489     // any view active?
0490     if (!m_mWin->activeView()) {
0491         return;
0492     }
0493 
0494     // look for the line
0495     QString linestr;
0496     int line;
0497     for (line = 0; line < m_mWin->activeView()->document()->lines(); line++) {
0498         linestr = m_mWin->activeView()->document()->line(line);
0499         if (linestr.indexOf(re) > -1) {
0500             break;
0501         }
0502     }
0503 
0504     // activate the line
0505     if (line != m_mWin->activeView()->document()->lines()) {
0506         // line found now look for the column
0507         int column = linestr.indexOf(word) + (word.length() / 2);
0508         m_mWin->activeView()->setCursorPosition(KTextEditor::Cursor(line, column));
0509     }
0510     m_mWin->activeView()->setFocus();
0511 }
0512 
0513 /******************************************************************/
0514 void KateCTagsView::startEditTmr()
0515 {
0516     if (m_ctagsUi.inputEdit->text().size() > 3) {
0517         m_editTimer.start(500);
0518     }
0519 }
0520 
0521 /******************************************************************/
0522 void KateCTagsView::updateSessionDB()
0523 {
0524     if (m_proc.state() != QProcess::NotRunning) {
0525         return;
0526     }
0527 
0528     QStringList targets;
0529     for (int i = 0; i < m_ctagsUi.targetList->count(); i++) {
0530         auto target = m_ctagsUi.targetList->item(i)->text();
0531         if (target.endsWith(QLatin1Char('/')) || target.endsWith(QLatin1Char('\\'))) {
0532             target = target.left(target.size() - 1);
0533         }
0534         targets << target;
0535     }
0536 
0537     QString pluginFolder = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1String("/katectags");
0538     QDir().mkpath(pluginFolder);
0539 
0540     if (m_ctagsUi.tagsFile->text().isEmpty()) {
0541         // FIXME we need a way to get the session name
0542         pluginFolder += QLatin1String("/session_db_");
0543         pluginFolder += QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd_hhmmss"));
0544         m_ctagsUi.tagsFile->setText(pluginFolder);
0545     }
0546 
0547     if (targets.isEmpty()) {
0548         Utils::showMessage(i18n("No folders or files to index"), QIcon(), i18n("CTags"), MessageType::Error);
0549         QFile::remove(m_ctagsUi.tagsFile->text());
0550         return;
0551     }
0552 
0553     QStringList arguments = m_proc.splitCommand(m_ctagsUi.cmdEdit->text());
0554     const QString command = arguments.takeFirst();
0555     arguments << QStringLiteral("-f") << m_ctagsUi.tagsFile->text() << targets;
0556     startHostProcess(m_proc, command, arguments);
0557 
0558     if (!m_proc.waitForStarted(500)) {
0559         Utils::showMessage(i18n("Failed to run. Error: %1, exit code: %2", m_proc.errorString(), m_proc.exitCode()),
0560                            QIcon(),
0561                            i18n("CTags"),
0562                            MessageType::Error);
0563         return;
0564     }
0565     QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
0566     m_ctagsUi.updateButton->setDisabled(true);
0567     m_ctagsUi.updateButton2->setDisabled(true);
0568 }
0569 
0570 /******************************************************************/
0571 void KateCTagsView::updateDone(int exitCode, QProcess::ExitStatus status)
0572 {
0573     if (status == QProcess::CrashExit) {
0574         Utils::showMessage(i18n("The CTags executable crashed", m_proc.errorString(), m_proc.exitCode()), QIcon(), i18n("CTags"), MessageType::Error);
0575     } else if (exitCode != 0) {
0576         Utils::showMessage(i18n("The CTags program exited with code %2: %1", QString::fromLocal8Bit(m_proc.readAllStandardError()), exitCode),
0577                            QIcon(),
0578                            i18n("CTags"),
0579                            MessageType::Error);
0580     }
0581 
0582     m_ctagsUi.updateButton->setDisabled(false);
0583     m_ctagsUi.updateButton2->setDisabled(false);
0584     QApplication::restoreOverrideCursor();
0585 }
0586 
0587 /******************************************************************/
0588 void KateCTagsView::addTagTarget()
0589 {
0590     QFileDialog dialog;
0591     dialog.setDirectory(QFileInfo(m_mWin->activeView()->document()->url().path()).path());
0592     dialog.setFileMode(QFileDialog::Directory);
0593 
0594     // i18n("CTags Database Location"));
0595     if (dialog.exec() != QDialog::Accepted) {
0596         return;
0597     }
0598 
0599     QStringList urls = dialog.selectedFiles();
0600 
0601     for (int i = 0; i < urls.size(); i++) {
0602         if (!listContains(urls[i])) {
0603             new QListWidgetItem(urls[i], m_ctagsUi.targetList);
0604         }
0605     }
0606 }
0607 
0608 /******************************************************************/
0609 void KateCTagsView::delTagTarget()
0610 {
0611     delete m_ctagsUi.targetList->currentItem();
0612 }
0613 
0614 /******************************************************************/
0615 bool KateCTagsView::listContains(const QString &target)
0616 {
0617     for (int i = 0; i < m_ctagsUi.targetList->count(); i++) {
0618         if (m_ctagsUi.targetList->item(i)->text() == target) {
0619             return true;
0620         }
0621     }
0622     return false;
0623 }
0624 
0625 /******************************************************************/
0626 bool KateCTagsView::eventFilter(QObject *obj, QEvent *event)
0627 {
0628     if (event->type() == QEvent::KeyPress) {
0629         QKeyEvent *ke = static_cast<QKeyEvent *>(event);
0630         if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) {
0631             m_mWin->hideToolView(m_toolView);
0632             event->accept();
0633             return true;
0634         }
0635     }
0636     return QObject::eventFilter(obj, event);
0637 }
0638 
0639 /******************************************************************/
0640 void KateCTagsView::resetCMD()
0641 {
0642     m_ctagsUi.cmdEdit->setText(DEFAULT_CTAGS_CMD);
0643 }
0644 
0645 /******************************************************************/
0646 void KateCTagsView::handleEsc(QEvent *e)
0647 {
0648     if (!m_mWin) {
0649         return;
0650     }
0651 
0652     QKeyEvent *k = static_cast<QKeyEvent *>(e);
0653     if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) {
0654         if (m_toolView->isVisible()) {
0655             m_mWin->hideToolView(m_toolView);
0656         }
0657     }
0658 }
0659 
0660 void KateCTagsView::showSymbols()
0661 {
0662     m_gotoSymbWidget->showSymbols(m_mWin->activeView()->document()->url().toLocalFile());
0663     m_gotoSymbWidget->show();
0664     m_gotoSymbWidget->setFocus();
0665 }
0666 
0667 void KateCTagsView::showGlobalSymbols()
0668 {
0669     m_gotoSymbWidget->showGlobalSymbols(m_ctagsUi.tagsFile->text());
0670     m_gotoSymbWidget->show();
0671     m_gotoSymbWidget->setFocus();
0672 }
0673 
0674 #include "moc_kate_ctags_view.cpp"