File indexing completed on 2024-04-14 03:55:11
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 "document.h" 0013 #include "kateconfig.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 QList<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 for (auto *view : KTextEditor::EditorPrivate::self()->views()) { 0171 if (view->statusBar() && view->statusBar()->modeMenu()) { 0172 view->statusBar()->modeMenu()->reloadItems(); 0173 } 0174 } 0175 } 0176 0177 // 0178 // save the given list to config file + update 0179 // 0180 void KateModeManager::save(const QList<KateFileType *> &v) 0181 { 0182 KConfig katerc(QStringLiteral("katemoderc"), KConfig::NoGlobals); 0183 0184 QStringList newg; 0185 newg.reserve(v.size()); 0186 for (const KateFileType *type : v) { 0187 KConfigGroup config(&katerc, type->name); 0188 0189 config.writeEntry("Section", type->section); 0190 config.writeXdgListEntry("Wildcards", type->wildcards); 0191 config.writeXdgListEntry("Mimetypes", type->mimetypes); 0192 config.writeEntry("Priority", type->priority); 0193 config.writeEntry("Indenter", type->indenter); 0194 0195 QString varLine = type->varLine; 0196 if (!varLine.contains(QLatin1String("kate:"))) { 0197 varLine.prepend(QLatin1String("kate: ")); 0198 } 0199 0200 config.writeEntry("Variables", varLine); 0201 0202 config.writeEntry("Highlighting", type->hl); 0203 0204 // only for generated types... 0205 config.writeEntry("Highlighting Generated", type->hlGenerated); 0206 config.writeEntry("Highlighting Version", type->version); 0207 0208 newg << type->name; 0209 } 0210 0211 const auto groupNames = katerc.groupList(); 0212 for (const QString &groupName : groupNames) { 0213 if (newg.indexOf(groupName) == -1) { 0214 katerc.deleteGroup(groupName); 0215 } 0216 } 0217 0218 katerc.sync(); 0219 0220 update(); 0221 } 0222 0223 QString KateModeManager::fileType(KTextEditor::Document *doc, const QString &fileToReadFrom) 0224 { 0225 if (!doc) { 0226 return QString(); 0227 } 0228 0229 if (m_types.isEmpty()) { 0230 return QString(); 0231 } 0232 0233 QString fileName = doc->url().toString(); 0234 int length = doc->url().toString().length(); 0235 0236 QString result; 0237 0238 // Try wildcards 0239 if (!fileName.isEmpty()) { 0240 static const QLatin1String commonSuffixes[] = { 0241 QLatin1String(".orig"), 0242 QLatin1String(".new"), 0243 QLatin1String("~"), 0244 QLatin1String(".bak"), 0245 QLatin1String(".BAK"), 0246 }; 0247 0248 if (!(result = wildcardsFind(fileName)).isEmpty()) { 0249 return result; 0250 } 0251 0252 QString backupSuffix = KateDocumentConfig::global()->backupSuffix(); 0253 if (fileName.endsWith(backupSuffix)) { 0254 if (!(result = wildcardsFind(fileName.left(length - backupSuffix.length()))).isEmpty()) { 0255 return result; 0256 } 0257 } 0258 0259 for (auto &commonSuffix : commonSuffixes) { 0260 if (commonSuffix != backupSuffix && fileName.endsWith(commonSuffix)) { 0261 if (!(result = wildcardsFind(fileName.left(length - commonSuffix.size()))).isEmpty()) { 0262 return result; 0263 } 0264 } 0265 } 0266 } 0267 0268 // either read the file passed to this function (pre-load) or use the normal mimeType() KF KTextEditor API 0269 QString mtName; 0270 if (!fileToReadFrom.isEmpty()) { 0271 mtName = QMimeDatabase().mimeTypeForFile(fileToReadFrom).name(); 0272 } else { 0273 mtName = doc->mimeType(); 0274 } 0275 return mimeTypesFind(mtName); 0276 } 0277 0278 template<typename UnaryStringPredicate> 0279 static QString findHighestPriorityTypeNameIf(const QList<KateFileType *> &types, QStringList KateFileType::*list, UnaryStringPredicate anyOfCondition) 0280 { 0281 const KateFileType *match = nullptr; 0282 auto matchPriority = std::numeric_limits<int>::lowest(); 0283 for (const KateFileType *type : types) { 0284 if (type->priority > matchPriority && std::any_of((type->*list).cbegin(), (type->*list).cend(), anyOfCondition)) { 0285 match = type; 0286 matchPriority = type->priority; 0287 } 0288 } 0289 return match == nullptr ? QString() : match->name; 0290 } 0291 0292 QString KateModeManager::wildcardsFind(const QString &fileName) const 0293 { 0294 const auto fileNameNoPath = QFileInfo{fileName}.fileName(); 0295 return findHighestPriorityTypeNameIf(m_types, &KateFileType::wildcards, [&fileNameNoPath](const QString &wildcard) { 0296 return KSyntaxHighlighting::WildcardMatcher::exactMatch(fileNameNoPath, wildcard); 0297 }); 0298 } 0299 0300 QString KateModeManager::mimeTypesFind(const QString &mimeTypeName) const 0301 { 0302 return findHighestPriorityTypeNameIf(m_types, &KateFileType::mimetypes, [&mimeTypeName](const QString &name) { 0303 return mimeTypeName == name; 0304 }); 0305 } 0306 0307 const KateFileType &KateModeManager::fileType(const QString &name) const 0308 { 0309 for (int i = 0; i < m_types.size(); ++i) { 0310 if (m_types[i]->name == name) { 0311 return *m_types[i]; 0312 } 0313 } 0314 0315 static KateFileType notype; 0316 return notype; 0317 }