File indexing completed on 2024-04-28 15:30:32
0001 /* 0002 SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 // BEGIN Includes 0008 #include "katemodemanager.h" 0009 #include "katemodemenulist.h" 0010 #include "katestatusbar.h" 0011 0012 #include "kateconfig.h" 0013 #include "katedocument.h" 0014 #include "kateglobal.h" 0015 #include "katepartdebug.h" 0016 #include "katesyntaxmanager.h" 0017 #include "kateview.h" 0018 0019 #include <KConfigGroup> 0020 #include <KSyntaxHighlighting/WildcardMatcher> 0021 0022 #include <QFileInfo> 0023 #include <QMimeDatabase> 0024 0025 #include <algorithm> 0026 #include <limits> 0027 // END Includes 0028 0029 static QStringList vectorToList(const QVector<QString> &v) 0030 { 0031 QStringList l; 0032 l.reserve(v.size()); 0033 std::copy(v.begin(), v.end(), std::back_inserter(l)); 0034 return l; 0035 } 0036 0037 KateModeManager::KateModeManager() 0038 { 0039 update(); 0040 } 0041 0042 KateModeManager::~KateModeManager() 0043 { 0044 qDeleteAll(m_types); 0045 } 0046 0047 bool compareKateFileType(const KateFileType *const left, const KateFileType *const right) 0048 { 0049 int comparison = left->translatedSection.compare(right->translatedSection, Qt::CaseInsensitive); 0050 if (comparison == 0) { 0051 comparison = left->translatedName.compare(right->translatedName, Qt::CaseInsensitive); 0052 } 0053 return comparison < 0; 0054 } 0055 0056 // 0057 // read the types from config file and update the internal list 0058 // 0059 void KateModeManager::update() 0060 { 0061 KConfig config(QStringLiteral("katemoderc"), KConfig::NoGlobals); 0062 0063 QStringList g(config.groupList()); 0064 0065 KateFileType *normalType = nullptr; 0066 qDeleteAll(m_types); 0067 m_types.clear(); 0068 m_name2Type.clear(); 0069 for (int z = 0; z < g.count(); z++) { 0070 KConfigGroup cg(&config, g[z]); 0071 0072 KateFileType *type = new KateFileType(); 0073 type->name = g[z]; 0074 type->wildcards = cg.readXdgListEntry(QStringLiteral("Wildcards")); 0075 type->mimetypes = cg.readXdgListEntry(QStringLiteral("Mimetypes")); 0076 type->priority = cg.readEntry(QStringLiteral("Priority"), 0); 0077 type->varLine = cg.readEntry(QStringLiteral("Variables")); 0078 type->indenter = cg.readEntry(QStringLiteral("Indenter")); 0079 0080 type->hl = cg.readEntry(QStringLiteral("Highlighting")); 0081 0082 // only for generated types... 0083 type->hlGenerated = cg.readEntry(QStringLiteral("Highlighting Generated"), false); 0084 0085 // the "Normal" mode will be added later 0086 if (type->name == QLatin1String("Normal")) { 0087 if (!normalType) { 0088 normalType = type; 0089 } 0090 } else { 0091 type->section = cg.readEntry(QStringLiteral("Section")); 0092 type->version = cg.readEntry(QStringLiteral("Highlighting Version")); 0093 0094 // already add all non-highlighting generated user types 0095 if (!type->hlGenerated) { 0096 m_types.append(type); 0097 } 0098 } 0099 0100 // insert into the hash... 0101 // NOTE: "katemoderc" could have modes that do not exist or are invalid (for example, custom 0102 // XML files that were deleted or renamed), so they will be added to the list "m_types" later 0103 m_name2Type.insert(type->name, type); 0104 } 0105 0106 // try if the hl stuff is up to date... 0107 const auto modes = KateHlManager::self()->modeList(); 0108 for (int i = 0; i < modes.size(); ++i) { 0109 // filter out hidden languages; and 0110 // filter out "None" hl, we add that later as "Normal" mode. 0111 // Hl with empty names will also be filtered. The 0112 // KTextEditor::DocumentPrivate::updateFileType() function considers 0113 // hl with empty names as invalid. 0114 if (modes[i].isHidden() || modes[i].name().isEmpty() || modes[i].name() == QLatin1String("None")) { 0115 continue; 0116 } 0117 0118 KateFileType *type = nullptr; 0119 bool newType = false; 0120 if (m_name2Type.contains(modes[i].name())) { 0121 type = m_name2Type[modes[i].name()]; 0122 0123 // add if hl generated, we skipped that stuff above 0124 if (type->hlGenerated) { 0125 m_types.append(type); 0126 } 0127 } else { 0128 newType = true; 0129 type = new KateFileType(); 0130 type->name = modes[i].name(); 0131 type->priority = 0; 0132 type->hlGenerated = true; 0133 m_types.append(type); 0134 m_name2Type.insert(type->name, type); 0135 } 0136 0137 if (newType || type->version != QString::number(modes[i].version())) { 0138 type->name = modes[i].name(); 0139 type->section = modes[i].section(); 0140 type->wildcards = vectorToList(modes[i].extensions()); 0141 type->mimetypes = vectorToList(modes[i].mimeTypes()); 0142 type->priority = modes[i].priority(); 0143 type->version = QString::number(modes[i].version()); 0144 type->indenter = modes[i].indenter(); 0145 type->hl = modes[i].name(); 0146 } 0147 0148 type->translatedName = modes[i].translatedName(); 0149 type->translatedSection = modes[i].translatedSection(); 0150 } 0151 0152 // sort the list... 0153 std::sort(m_types.begin(), m_types.end(), compareKateFileType); 0154 0155 // add the none type... 0156 if (!normalType) { 0157 normalType = new KateFileType(); 0158 } 0159 0160 // marked by hlGenerated 0161 normalType->name = QStringLiteral("Normal"); 0162 normalType->translatedName = i18n("Normal"); 0163 normalType->hl = QStringLiteral("None"); 0164 normalType->hlGenerated = true; 0165 0166 m_types.prepend(normalType); 0167 0168 // update the mode menu of the status bar, for all views. 0169 // this menu uses the KateFileType objects 0170 const auto views = KTextEditor::EditorPrivate::self()->views(); 0171 for (auto *view : views) { 0172 if (view->statusBar() && view->statusBar()->modeMenu()) { 0173 view->statusBar()->modeMenu()->reloadItems(); 0174 } 0175 } 0176 } 0177 0178 // 0179 // save the given list to config file + update 0180 // 0181 void KateModeManager::save(const QList<KateFileType *> &v) 0182 { 0183 KConfig katerc(QStringLiteral("katemoderc"), KConfig::NoGlobals); 0184 0185 QStringList newg; 0186 newg.reserve(v.size()); 0187 for (const KateFileType *type : v) { 0188 KConfigGroup config(&katerc, type->name); 0189 0190 config.writeEntry("Section", type->section); 0191 config.writeXdgListEntry("Wildcards", type->wildcards); 0192 config.writeXdgListEntry("Mimetypes", type->mimetypes); 0193 config.writeEntry("Priority", type->priority); 0194 config.writeEntry("Indenter", type->indenter); 0195 0196 QString varLine = type->varLine; 0197 if (!varLine.contains(QLatin1String("kate:"))) { 0198 varLine.prepend(QLatin1String("kate: ")); 0199 } 0200 0201 config.writeEntry("Variables", varLine); 0202 0203 config.writeEntry("Highlighting", type->hl); 0204 0205 // only for generated types... 0206 config.writeEntry("Highlighting Generated", type->hlGenerated); 0207 config.writeEntry("Highlighting Version", type->version); 0208 0209 newg << type->name; 0210 } 0211 0212 const auto groupNames = katerc.groupList(); 0213 for (const QString &groupName : groupNames) { 0214 if (newg.indexOf(groupName) == -1) { 0215 katerc.deleteGroup(groupName); 0216 } 0217 } 0218 0219 katerc.sync(); 0220 0221 update(); 0222 } 0223 0224 QString KateModeManager::fileType(KTextEditor::DocumentPrivate *doc, const QString &fileToReadFrom) 0225 { 0226 if (!doc) { 0227 return QString(); 0228 } 0229 0230 if (m_types.isEmpty()) { 0231 return QString(); 0232 } 0233 0234 QString fileName = doc->url().toString(); 0235 int length = doc->url().toString().length(); 0236 0237 QString result; 0238 0239 // Try wildcards 0240 if (!fileName.isEmpty()) { 0241 static const QLatin1String commonSuffixes[] = { 0242 QLatin1String(".orig"), 0243 QLatin1String(".new"), 0244 QLatin1String("~"), 0245 QLatin1String(".bak"), 0246 QLatin1String(".BAK"), 0247 }; 0248 0249 if (!(result = wildcardsFind(fileName)).isEmpty()) { 0250 return result; 0251 } 0252 0253 QString backupSuffix = KateDocumentConfig::global()->backupSuffix(); 0254 if (fileName.endsWith(backupSuffix)) { 0255 if (!(result = wildcardsFind(fileName.left(length - backupSuffix.length()))).isEmpty()) { 0256 return result; 0257 } 0258 } 0259 0260 for (auto &commonSuffix : commonSuffixes) { 0261 if (commonSuffix != backupSuffix && fileName.endsWith(commonSuffix)) { 0262 if (!(result = wildcardsFind(fileName.left(length - commonSuffix.size()))).isEmpty()) { 0263 return result; 0264 } 0265 } 0266 } 0267 } 0268 0269 // either read the file passed to this function (pre-load) or use the normal mimeType() KF KTextEditor API 0270 QString mtName; 0271 if (!fileToReadFrom.isEmpty()) { 0272 mtName = QMimeDatabase().mimeTypeForFile(fileToReadFrom).name(); 0273 } else { 0274 mtName = doc->mimeType(); 0275 } 0276 return mimeTypesFind(mtName); 0277 } 0278 0279 template<typename UnaryStringPredicate> 0280 static QString findHighestPriorityTypeNameIf(const QList<KateFileType *> &types, QStringList KateFileType::*list, UnaryStringPredicate anyOfCondition) 0281 { 0282 const KateFileType *match = nullptr; 0283 auto matchPriority = std::numeric_limits<int>::lowest(); 0284 for (const KateFileType *type : types) { 0285 if (type->priority > matchPriority && std::any_of((type->*list).cbegin(), (type->*list).cend(), anyOfCondition)) { 0286 match = type; 0287 matchPriority = type->priority; 0288 } 0289 } 0290 return match == nullptr ? QString() : match->name; 0291 } 0292 0293 QString KateModeManager::wildcardsFind(const QString &fileName) const 0294 { 0295 const auto fileNameNoPath = QFileInfo{fileName}.fileName(); 0296 return findHighestPriorityTypeNameIf(m_types, &KateFileType::wildcards, [&fileNameNoPath](const QString &wildcard) { 0297 return KSyntaxHighlighting::WildcardMatcher::exactMatch(fileNameNoPath, wildcard); 0298 }); 0299 } 0300 0301 QString KateModeManager::mimeTypesFind(const QString &mimeTypeName) const 0302 { 0303 return findHighestPriorityTypeNameIf(m_types, &KateFileType::mimetypes, [&mimeTypeName](const QString &name) { 0304 return mimeTypeName == name; 0305 }); 0306 } 0307 0308 const KateFileType &KateModeManager::fileType(const QString &name) const 0309 { 0310 for (int i = 0; i < m_types.size(); ++i) { 0311 if (m_types[i]->name == name) { 0312 return *m_types[i]; 0313 } 0314 } 0315 0316 static KateFileType notype; 0317 return notype; 0318 }