File indexing completed on 2024-05-05 05:51:20
0001 /* 0002 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #include "cmakecompletion.h" 0007 0008 #include "hostprocess.h" 0009 0010 #include <KTextEditor/Document> 0011 #include <KTextEditor/View> 0012 0013 #include <QProcess> 0014 #include <QStandardPaths> 0015 0016 struct CMakeComplData { 0017 std::vector<QByteArray> m_commands; 0018 std::vector<QByteArray> m_vars; 0019 std::vector<QByteArray> m_props; 0020 }; 0021 0022 static QByteArray runCMake(const QString &arg) 0023 { 0024 static const auto cmakeExecutable = safeExecutableName(QStringLiteral("cmake")); 0025 if (cmakeExecutable.isEmpty()) { 0026 return {}; 0027 } 0028 0029 QProcess p; 0030 startHostProcess(p, cmakeExecutable, {arg}); 0031 if (p.waitForStarted() && p.waitForFinished()) { 0032 if (p.exitCode() == 0 && p.exitStatus() == QProcess::NormalExit) { 0033 return p.readAllStandardOutput(); 0034 } 0035 } 0036 return {}; 0037 } 0038 0039 std::vector<QByteArray> parseList(const QByteArray &ba, int reserve) 0040 { 0041 std::vector<QByteArray> ret; 0042 ret.reserve(reserve); 0043 int start = 0; 0044 int next = ba.indexOf('\n', start); 0045 0046 while (next > 0) { 0047 ret.push_back(ba.mid(start, next - start)); 0048 start = next + 1; 0049 next = ba.indexOf('\n', start); 0050 } 0051 return ret; 0052 } 0053 0054 static CMakeComplData fetchData() 0055 { 0056 CMakeComplData ret; 0057 0058 auto cmds = runCMake(QStringLiteral("--help-command-list")); 0059 auto vars = runCMake(QStringLiteral("--help-variable-list")); 0060 auto props = runCMake(QStringLiteral("--help-property-list")); 0061 0062 // The numbers are from counting the number of props/vars/cmds 0063 // from the output of cmake --help-* 0064 ret.m_commands = parseList(cmds, 125); 0065 ret.m_vars = parseList(vars, 627); 0066 ret.m_props = parseList(props, 497); 0067 0068 return ret; 0069 } 0070 0071 bool CMakeCompletion::isCMakeFile(const QUrl &url) 0072 { 0073 auto urlString = url.fileName(); 0074 return urlString == QStringLiteral("CMakeLists.txt") || urlString.endsWith(QStringLiteral(".cmake")); 0075 } 0076 0077 static void append(std::vector<CMakeCompletion::Completion> &out, std::vector<QByteArray> &&in, CMakeCompletion::Completion::Kind kind) 0078 { 0079 for (auto &&s : in) { 0080 out.push_back({kind, std::move(s)}); 0081 } 0082 } 0083 0084 CMakeCompletion::CMakeCompletion(QObject *parent) 0085 : KTextEditor::CodeCompletionModel(parent) 0086 { 0087 } 0088 0089 void CMakeCompletion::completionInvoked(KTextEditor::View *view, const KTextEditor::Range &, InvocationType) 0090 { 0091 if (!m_hasData && isCMakeFile(view->document()->url())) { 0092 CMakeComplData data = fetchData(); 0093 append(m_matches, std::move(data.m_commands), Completion::Compl_COMMAND); 0094 append(m_matches, std::move(data.m_vars), Completion::Compl_VARIABLE); 0095 append(m_matches, std::move(data.m_props), Completion::Compl_PROPERTY); 0096 setRowCount(m_matches.size()); 0097 m_hasData = true; 0098 } 0099 } 0100 0101 bool CMakeCompletion::shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) 0102 { 0103 if (!userInsertion) { 0104 return false; 0105 } 0106 if (insertedText.isEmpty()) { 0107 return false; 0108 } 0109 // Dont invoke for comments, wont handle everything of course 0110 if (view->document()->line(position.line()).startsWith(QLatin1Char('#'))) { 0111 return false; 0112 } 0113 0114 return isCMakeFile(view->document()->url()); 0115 } 0116 0117 int CMakeCompletion::rowCount(const QModelIndex &parent) const 0118 { 0119 return parent.isValid() ? 0 : m_matches.size(); 0120 } 0121 0122 static QIcon getIcon(CMakeCompletion::Completion::Kind type) 0123 { 0124 using Kind = CMakeCompletion::Completion::Kind; 0125 if (type == Kind::Compl_PROPERTY) { 0126 static const QIcon icon(QIcon::fromTheme(QStringLiteral("code-block"))); 0127 return icon; 0128 } else if (type == Kind::Compl_COMMAND) { 0129 static const QIcon icon(QIcon::fromTheme(QStringLiteral("code-function"))); 0130 return icon; 0131 } else if (type == Kind::Compl_VARIABLE) { 0132 static const QIcon icon(QIcon::fromTheme(QStringLiteral("code-variable"))); 0133 return icon; 0134 } else { 0135 Q_UNREACHABLE(); 0136 return {}; 0137 } 0138 } 0139 0140 QVariant CMakeCompletion::data(const QModelIndex &index, int role) const 0141 { 0142 if (!index.isValid()) 0143 return {}; 0144 0145 if (role != Qt::DisplayRole && role != Qt::DecorationRole) 0146 return {}; 0147 0148 if (index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole) { 0149 return m_matches.at(index.row()).text; 0150 } else if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) { 0151 return getIcon(m_matches.at(index.row()).kind); 0152 } 0153 0154 return {}; 0155 } 0156 0157 #include "moc_cmakecompletion.cpp"