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

0001 /*
0002     SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "gotosymbolmodel.h"
0007 
0008 #include "hostprocess.h"
0009 
0010 #include <KLocalizedString>
0011 #include <QProcess>
0012 #include <QStandardPaths>
0013 
0014 GotoSymbolModel::GotoSymbolModel(QObject *parent)
0015     : QAbstractTableModel(parent)
0016 {
0017 }
0018 
0019 int GotoSymbolModel::columnCount(const QModelIndex &) const
0020 {
0021     return 1;
0022 }
0023 
0024 int GotoSymbolModel::rowCount(const QModelIndex &) const
0025 {
0026     return m_rows.count();
0027 }
0028 
0029 QVariant GotoSymbolModel::data(const QModelIndex &index, int role) const
0030 {
0031     if (!index.isValid()) {
0032         return {};
0033     }
0034 
0035     const auto &row = m_rows.at(index.row());
0036     if (role == Qt::DisplayRole) {
0037         if (index.column() == 0) {
0038             return row.name;
0039         }
0040     } else if (role == Qt::DecorationRole) {
0041         if (index.column() == 0) {
0042             return row.icon;
0043         }
0044     } else if (role == Qt::UserRole) {
0045         return row.line;
0046     }
0047 
0048     return QVariant();
0049 }
0050 
0051 void GotoSymbolModel::refresh(const QString &filePath)
0052 {
0053     static const QIcon nsIcon = QIcon::fromTheme(QStringLiteral("code-block"));
0054     static const QIcon classIcon = QIcon::fromTheme(QStringLiteral("code-class"));
0055     static const QIcon funcIcon = QIcon::fromTheme(QStringLiteral("code-function"));
0056     static const QIcon varIcon = QIcon::fromTheme(QStringLiteral("code-variable"));
0057     static const QIcon defIcon = nsIcon;
0058 
0059     beginResetModel();
0060     m_rows.clear();
0061     endResetModel();
0062 
0063     // only use ctags from PATH
0064     static const auto fullExecutablePath = safeExecutableName(QStringLiteral("ctags"));
0065     if (fullExecutablePath.isEmpty()) {
0066         beginResetModel();
0067         m_rows.append(SymbolItem{i18n("CTags executable not found."), -1, QIcon()});
0068         endResetModel();
0069         return;
0070     }
0071 
0072     QProcess p;
0073     startHostProcess(p, fullExecutablePath, {QStringLiteral("-x"), QStringLiteral("--_xformat=%{name}%{signature}\t%{kind}\t%{line}"), filePath});
0074 
0075     QByteArray out;
0076     if (p.waitForFinished()) {
0077         out = p.readAllStandardOutput();
0078     } else {
0079         beginResetModel();
0080         m_rows.append(SymbolItem{i18n("CTags executable failed to execute."), -1, QIcon()});
0081         endResetModel();
0082         return;
0083     }
0084 
0085     QList<SymbolItem> symItems;
0086     const auto tags = out.split('\n');
0087     symItems.reserve(tags.size());
0088     for (const auto &tag : tags) {
0089         const auto items = tag.split('\t');
0090         if (items.isEmpty() || items.count() < 3) {
0091             continue;
0092         }
0093 
0094         SymbolItem item;
0095         item.name = QLatin1String(items.at(0));
0096         // this happens in markdown names for some reason
0097         if (item.name.endsWith(QLatin1Char('-'))) {
0098             item.name.chop(1);
0099         }
0100 
0101         switch (items.at(1).at(0)) {
0102         case 'f':
0103             item.icon = funcIcon;
0104             break;
0105         case 'm':
0106             if (items.at(1) == "method") {
0107                 item.icon = funcIcon;
0108             } else {
0109                 item.icon = defIcon;
0110             }
0111             break;
0112         case 'g':
0113             if (items.at(1) == "getter") {
0114                 item.icon = funcIcon;
0115             } else {
0116                 item.icon = defIcon;
0117             }
0118             break;
0119         case 'c':
0120         case 's':
0121             if (items.at(1) == "class" || items.at(1) == "struct") {
0122                 item.icon = classIcon;
0123             } else {
0124                 item.icon = defIcon;
0125             }
0126             break;
0127         case 'n':
0128             if (items.at(1) == "namespace") {
0129                 item.icon = nsIcon;
0130             }
0131             break;
0132         case 'v':
0133             item.icon = varIcon;
0134             break;
0135         default:
0136             item.icon = defIcon;
0137             break;
0138         }
0139 
0140         item.line = items.at(2).toInt();
0141         symItems.append(item);
0142     }
0143 
0144     beginResetModel();
0145     if (!symItems.isEmpty()) {
0146         m_rows = std::move(symItems);
0147     } else {
0148         m_rows.append(SymbolItem{i18n("CTags was unable to parse this file."), -1, QIcon()});
0149     }
0150     endResetModel();
0151 }
0152 
0153 #include "moc_gotosymbolmodel.cpp"