File indexing completed on 2024-05-05 05:51:20
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2008-2014 Dominik Haumann <dhaumann kde org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 // BEGIN Includes 0008 #include "katebacktracebrowser.h" 0009 0010 #include "btparser.h" 0011 0012 #include <KConfigGroup> 0013 #include <KLineEdit> 0014 #include <KLocalizedString> // i18n 0015 #include <KPluginFactory> 0016 #include <KSharedConfig> 0017 0018 #include <KTextEditor/Cursor> 0019 #include <KTextEditor/View> 0020 0021 #include <QClipboard> 0022 #include <QDialogButtonBox> 0023 #include <QDir> 0024 #include <QFile> 0025 #include <QFileDialog> 0026 #include <QFileInfo> 0027 #include <QStandardPaths> 0028 #include <QTreeWidget> 0029 #include <QUrl> 0030 // END Includes 0031 0032 K_PLUGIN_FACTORY_WITH_JSON(KateBtBrowserFactory, "katebacktracebrowserplugin.json", registerPlugin<KateBtBrowserPlugin>();) 0033 0034 KateBtBrowserPlugin *KateBtBrowserPlugin::s_self = nullptr; 0035 static QStringList fileExtensions = QStringList() << QStringLiteral("*.cpp") << QStringLiteral("*.cxx") << QStringLiteral("*.c") << QStringLiteral("*.cc") 0036 << QStringLiteral("*.h") << QStringLiteral("*.hpp") << QStringLiteral("*.hxx") << QStringLiteral("*.moc"); 0037 0038 KateBtBrowserPlugin::KateBtBrowserPlugin(QObject *parent, const QVariantList &) 0039 : KTextEditor::Plugin(parent) 0040 , indexer(&db) 0041 { 0042 s_self = this; 0043 db.loadFromFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/katebtbrowser/backtracedatabase.db")); 0044 } 0045 0046 KateBtBrowserPlugin::~KateBtBrowserPlugin() 0047 { 0048 if (indexer.isRunning()) { 0049 indexer.cancel(); 0050 indexer.wait(); 0051 } 0052 0053 const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/katebtbrowser"); 0054 QDir().mkpath(path); 0055 db.saveToFile(path + QStringLiteral("/backtracedatabase.db")); 0056 0057 s_self = nullptr; 0058 } 0059 0060 KateBtBrowserPlugin &KateBtBrowserPlugin::self() 0061 { 0062 return *s_self; 0063 } 0064 0065 QObject *KateBtBrowserPlugin::createView(KTextEditor::MainWindow *mainWindow) 0066 { 0067 KateBtBrowserPluginView *view = new KateBtBrowserPluginView(this, mainWindow); 0068 return view; 0069 } 0070 0071 KateBtDatabase &KateBtBrowserPlugin::database() 0072 { 0073 return db; 0074 } 0075 0076 BtFileIndexer &KateBtBrowserPlugin::fileIndexer() 0077 { 0078 return indexer; 0079 } 0080 0081 void KateBtBrowserPlugin::startIndexer() 0082 { 0083 if (indexer.isRunning()) { 0084 indexer.cancel(); 0085 indexer.wait(); 0086 } 0087 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("backtracebrowser")); 0088 indexer.setSearchPaths(cg.readEntry("search-folders", QStringList())); 0089 indexer.setFilter(cg.readEntry("file-extensions", fileExtensions)); 0090 indexer.start(); 0091 Q_EMIT newStatus(i18n("Indexing files...")); 0092 } 0093 0094 int KateBtBrowserPlugin::configPages() const 0095 { 0096 return 1; 0097 } 0098 0099 KTextEditor::ConfigPage *KateBtBrowserPlugin::configPage(int number, QWidget *parent) 0100 { 0101 if (number == 0) { 0102 return new KateBtConfigWidget(parent); 0103 } 0104 0105 return nullptr; 0106 } 0107 0108 KateBtBrowserPluginView::KateBtBrowserPluginView(KateBtBrowserPlugin *plugin, KTextEditor::MainWindow *mainWindow) 0109 : QObject(mainWindow) 0110 { 0111 // init console 0112 QWidget *toolview = mainWindow->createToolView(plugin, 0113 QStringLiteral("kate_private_plugin_katebacktracebrowserplugin"), 0114 KTextEditor::MainWindow::Bottom, 0115 QIcon::fromTheme(QStringLiteral("tools-report-bug")), 0116 i18n("Backtrace")); 0117 m_widget = new KateBtBrowserWidget(mainWindow, toolview); 0118 0119 connect(plugin, &KateBtBrowserPlugin::newStatus, m_widget, &KateBtBrowserWidget::setStatus); 0120 } 0121 0122 KateBtBrowserPluginView::~KateBtBrowserPluginView() 0123 { 0124 // cleanup, kill toolview + widget 0125 auto toolview = m_widget->parent(); 0126 delete m_widget; 0127 delete toolview; 0128 } 0129 0130 KateBtBrowserWidget::KateBtBrowserWidget(KTextEditor::MainWindow *mainwindow, QWidget *parent) 0131 : QWidget(parent) 0132 , mw(mainwindow) 0133 { 0134 setupUi(this); 0135 0136 timer.setSingleShot(true); 0137 connect(&timer, &QTimer::timeout, this, &KateBtBrowserWidget::clearStatus); 0138 connect(btnBacktrace, &QPushButton::clicked, this, &KateBtBrowserWidget::loadFile); 0139 connect(btnClipboard, &QPushButton::clicked, this, &KateBtBrowserWidget::loadClipboard); 0140 connect(btnConfigure, &QPushButton::clicked, this, &KateBtBrowserWidget::configure); 0141 connect(lstBacktrace, &QTreeWidget::itemActivated, this, &KateBtBrowserWidget::itemActivated); 0142 } 0143 0144 KateBtBrowserWidget::~KateBtBrowserWidget() 0145 { 0146 } 0147 0148 void KateBtBrowserWidget::loadFile() 0149 { 0150 QString url = QFileDialog::getOpenFileName(mw->window(), i18n("Load Backtrace")); 0151 QFile f(url); 0152 if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { 0153 QString str = QString::fromUtf8(f.readAll()); 0154 loadBacktrace(str); 0155 } 0156 } 0157 0158 void KateBtBrowserWidget::loadClipboard() 0159 { 0160 QString bt = QApplication::clipboard()->text(); 0161 loadBacktrace(bt); 0162 } 0163 0164 void KateBtBrowserWidget::loadBacktrace(const QString &bt) 0165 { 0166 const QList<BtInfo> infos = KateBtParser::parseBacktrace(bt); 0167 0168 lstBacktrace->clear(); 0169 for (const BtInfo &info : infos) { 0170 QTreeWidgetItem *it = new QTreeWidgetItem(lstBacktrace); 0171 it->setData(0, Qt::DisplayRole, QString::number(info.step)); 0172 it->setData(0, Qt::ToolTipRole, QString::number(info.step)); 0173 QFileInfo fi(info.filename); 0174 it->setData(1, Qt::DisplayRole, fi.fileName()); 0175 it->setData(1, Qt::ToolTipRole, info.filename); 0176 0177 if (info.type == BtInfo::Source) { 0178 it->setData(2, Qt::DisplayRole, QString::number(info.line)); 0179 it->setData(2, Qt::ToolTipRole, QString::number(info.line)); 0180 it->setData(2, Qt::UserRole, QVariant(info.line)); 0181 } 0182 it->setData(3, Qt::DisplayRole, info.function); 0183 it->setData(3, Qt::ToolTipRole, info.function); 0184 0185 lstBacktrace->addTopLevelItem(it); 0186 } 0187 lstBacktrace->resizeColumnToContents(0); 0188 lstBacktrace->resizeColumnToContents(1); 0189 lstBacktrace->resizeColumnToContents(2); 0190 0191 if (lstBacktrace->topLevelItemCount()) { 0192 setStatus(i18n("Loading backtrace succeeded")); 0193 } else { 0194 setStatus(i18n("Loading backtrace failed")); 0195 } 0196 } 0197 0198 void KateBtBrowserWidget::configure() 0199 { 0200 KateBtConfigDialog dlg(mw->window()); 0201 dlg.exec(); 0202 } 0203 0204 void KateBtBrowserWidget::itemActivated(QTreeWidgetItem *item, int column) 0205 { 0206 Q_UNUSED(column); 0207 0208 QVariant variant = item->data(2, Qt::UserRole); 0209 if (variant.isValid()) { 0210 int line = variant.toInt(); 0211 QString file = QDir::fromNativeSeparators(item->data(1, Qt::ToolTipRole).toString()); 0212 file = QDir::cleanPath(file); 0213 0214 QString path = file; 0215 // if not absolute path + exists, try to find with index 0216 if (!QFile::exists(path)) { 0217 // try to match the backtrace forms ".*/foo/bar.txt" and "foo/bar.txt" 0218 static const QRegularExpression rx1(QStringLiteral("/([^/]+)/([^/]+)$")); 0219 QRegularExpressionMatch match = rx1.match(file); 0220 if (match.hasMatch()) { 0221 file = match.captured(1) + QLatin1Char('/') + match.captured(2); 0222 } else { 0223 static const QRegularExpression rx2(QStringLiteral("([^/]+)/([^/]+)$")); 0224 if (rx2.match(file).hasMatch()) { 0225 // file is of correct form 0226 } else { 0227 qDebug() << "file patter did not match:" << file; 0228 setStatus(i18n("File not found: %1", file)); 0229 return; 0230 } 0231 } 0232 path = KateBtBrowserPlugin::self().database().value(file); 0233 } 0234 0235 if (!path.isEmpty() && QFile::exists(path)) { 0236 KTextEditor::View *kv = mw->openUrl(QUrl::fromLocalFile(path)); 0237 kv->setCursorPosition(KTextEditor::Cursor(line - 1, 0)); 0238 kv->setFocus(); 0239 setStatus(i18n("Opened file: %1", file)); 0240 } 0241 } else { 0242 setStatus(i18n("No debugging information available")); 0243 } 0244 } 0245 0246 void KateBtBrowserWidget::setStatus(const QString &status) 0247 { 0248 lblStatus->setText(status); 0249 timer.start(10 * 1000); 0250 } 0251 0252 void KateBtBrowserWidget::clearStatus() 0253 { 0254 lblStatus->setText(QString()); 0255 } 0256 0257 KateBtConfigWidget::KateBtConfigWidget(QWidget *parent) 0258 : KTextEditor::ConfigPage(parent) 0259 { 0260 setupUi(this); 0261 edtUrl->setMode(KFile::Directory); 0262 edtUrl->setUrl(QUrl(QDir().absolutePath())); 0263 0264 reset(); 0265 0266 connect(btnAdd, &QPushButton::clicked, this, &KateBtConfigWidget::add); 0267 connect(btnRemove, &QPushButton::clicked, this, &KateBtConfigWidget::remove); 0268 connect(edtExtensions, &QLineEdit::textChanged, this, &KateBtConfigWidget::textChanged); 0269 0270 m_changed = false; 0271 } 0272 0273 KateBtConfigWidget::~KateBtConfigWidget() 0274 { 0275 } 0276 0277 QString KateBtConfigWidget::name() const 0278 { 0279 return i18n("Backtrace"); 0280 } 0281 0282 QString KateBtConfigWidget::fullName() const 0283 { 0284 return i18n("Backtrace Settings"); 0285 } 0286 0287 QIcon KateBtConfigWidget::icon() const 0288 { 0289 return QIcon::fromTheme(QStringLiteral("tools-report-bug")); 0290 } 0291 0292 void KateBtConfigWidget::apply() 0293 { 0294 if (m_changed) { 0295 QStringList sl; 0296 for (int i = 0; i < lstFolders->count(); ++i) { 0297 sl << lstFolders->item(i)->data(Qt::DisplayRole).toString(); 0298 } 0299 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("backtracebrowser")); 0300 cg.writeEntry("search-folders", sl); 0301 0302 QString filter = edtExtensions->text(); 0303 filter.replace(QLatin1Char(','), QLatin1Char(' ')).replace(QLatin1Char(';'), QLatin1Char(' ')); 0304 cg.writeEntry("file-extensions", filter.split(QLatin1Char(' '), Qt::SkipEmptyParts)); 0305 0306 KateBtBrowserPlugin::self().startIndexer(); 0307 m_changed = false; 0308 } 0309 } 0310 0311 void KateBtConfigWidget::reset() 0312 { 0313 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("backtracebrowser")); 0314 lstFolders->clear(); 0315 lstFolders->addItems(cg.readEntry("search-folders", QStringList())); 0316 edtExtensions->setText(cg.readEntry("file-extensions", fileExtensions).join(QLatin1Char(' '))); 0317 } 0318 0319 void KateBtConfigWidget::defaults() 0320 { 0321 lstFolders->clear(); 0322 edtExtensions->setText(fileExtensions.join(QLatin1Char(' '))); 0323 0324 m_changed = true; 0325 } 0326 0327 void KateBtConfigWidget::add() 0328 { 0329 QDir url(edtUrl->lineEdit()->text()); 0330 if (url.exists()) { 0331 if (lstFolders->findItems(url.absolutePath(), Qt::MatchExactly).empty()) { 0332 lstFolders->addItem(url.absolutePath()); 0333 Q_EMIT changed(); 0334 m_changed = true; 0335 } 0336 } 0337 } 0338 0339 void KateBtConfigWidget::remove() 0340 { 0341 QListWidgetItem *item = lstFolders->currentItem(); 0342 if (item) { 0343 delete item; 0344 Q_EMIT changed(); 0345 m_changed = true; 0346 } 0347 } 0348 0349 void KateBtConfigWidget::textChanged() 0350 { 0351 Q_EMIT changed(); 0352 m_changed = true; 0353 } 0354 0355 KateBtConfigDialog::KateBtConfigDialog(QWidget *parent) 0356 : QDialog(parent) 0357 { 0358 setWindowTitle(i18n("Backtrace Browser Settings")); 0359 0360 m_configWidget = new KateBtConfigWidget(this); 0361 0362 QVBoxLayout *layout = new QVBoxLayout(this); 0363 0364 QDialogButtonBox *box = new QDialogButtonBox(this); 0365 box->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0366 0367 layout->addWidget(m_configWidget); 0368 layout->addWidget(box); 0369 0370 connect(this, &KateBtConfigDialog::accepted, m_configWidget, &KateBtConfigWidget::apply); 0371 connect(box, &QDialogButtonBox::accepted, this, &KateBtConfigDialog::accept); 0372 connect(box, &QDialogButtonBox::rejected, this, &KateBtConfigDialog::reject); 0373 } 0374 0375 KateBtConfigDialog::~KateBtConfigDialog() 0376 { 0377 } 0378 0379 #include "katebacktracebrowser.moc" 0380 #include "moc_katebacktracebrowser.cpp" 0381 0382 // kate: space-indent on; indent-width 4; replace-tabs on;