diff options
Diffstat (limited to 'src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp')
-rw-r--r-- | src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp new file mode 100644 index 0000000000..6b2fabd07a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp @@ -0,0 +1,325 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "repository.h" +#include "repository_p.h" +#include "definition.h" +#include "definition_p.h" +#include "theme.h" +#include "themedata_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "wildcardmatcher_p.h" + +#include <QDebug> +#include <QDirIterator> +#include <QFile> +#include <QFileInfo> +#include <QJsonDocument> +#include <QJsonObject> + +#ifndef NO_STANDARD_PATHS +#include <QStandardPaths> +#endif + +#include <limits> + +using namespace KSyntaxHighlighting; + +static void initResource() +{ +#ifdef HAS_SYNTAX_RESOURCE + Q_INIT_RESOURCE(syntax_data); +#endif + Q_INIT_RESOURCE(theme_data); +} + +RepositoryPrivate* RepositoryPrivate::get(Repository *repo) +{ + return repo->d.get(); +} + +Repository::Repository() : + d(new RepositoryPrivate) +{ + initResource(); + d->load(this); +} + +Repository::~Repository() +{ + // reset repo so we can detect in still alive definition instances + // that the repo was deleted + foreach (const auto &def, d->m_sortedDefs) + DefinitionData::get(def)->repo = nullptr; +} + +Definition Repository::definitionForName(const QString& defName) const +{ + return d->m_defs.value(defName); +} + +static Definition bestCandidate(QVector<Definition>& candidates) +{ + if (candidates.isEmpty()) + return Definition(); + + std::partial_sort(candidates.begin(), candidates.begin() + 1, candidates.end(), [](const Definition &lhs, const Definition &rhs) { + return lhs.priority() > rhs.priority(); + }); + + return candidates.at(0); +} + +Definition Repository::definitionForFileName(const QString& fileName) const +{ + QFileInfo fi(fileName); + const auto name = fi.fileName(); + + QVector<Definition> candidates; + for (auto it = d->m_defs.constBegin(); it != d->m_defs.constEnd(); ++it) { + auto def = it.value(); + foreach (const auto &pattern, def.extensions()) { + if (WildcardMatcher::exactMatch(name, pattern)) { + candidates.push_back(def); + break; + } + } + } + + return bestCandidate(candidates); +} + +Definition Repository::definitionForMimeType(const QString& mimeType) const +{ + QVector<Definition> candidates; + for (auto it = d->m_defs.constBegin(); it != d->m_defs.constEnd(); ++it) { + auto def = it.value(); + foreach (const auto &matchType, def.mimeTypes()) { + if (mimeType == matchType) { + candidates.push_back(def); + break; + } + } + } + + return bestCandidate(candidates); +} + +QVector<Definition> Repository::definitions() const +{ + return d->m_sortedDefs; +} + +QVector<Theme> Repository::themes() const +{ + return d->m_themes; +} + +Theme Repository::theme(const QString &themeName) const +{ + for (const auto &theme : qAsConst(d->m_themes)) { + if (theme.name() == themeName) { + return theme; + } + } + + return Theme(); +} + +Theme Repository::defaultTheme(Repository::DefaultTheme t) +{ + if (t == DarkTheme) + return theme(QLatin1String("Breeze Dark")); + return theme(QLatin1String("Default")); +} + +void RepositoryPrivate::load(Repository *repo) +{ + // always add invalid default "None" highlighting + addDefinition(Definition()); + + // do lookup in standard paths, if not disabled +#ifndef NO_STANDARD_PATHS + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/syntax"), QStandardPaths::LocateDirectory)) + loadSyntaxFolder(repo, dir); + + // backward compatibility with Kate + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory)) + loadSyntaxFolder(repo, dir); +#endif + + // default resources are always used + loadSyntaxFolder(repo, QStringLiteral(":/org.kde.syntax-highlighting/syntax")); + + // user given extra paths + foreach (const auto &path, m_customSearchPaths) + loadSyntaxFolder(repo, path + QStringLiteral("/syntax")); + + m_sortedDefs.reserve(m_defs.size()); + for (auto it = m_defs.constBegin(); it != m_defs.constEnd(); ++it) + m_sortedDefs.push_back(it.value()); + std::sort(m_sortedDefs.begin(), m_sortedDefs.end(), [](const Definition &left, const Definition &right) { + auto comparison = left.translatedSection().compare(right.translatedSection(), Qt::CaseInsensitive); + if (comparison == 0) + comparison = left.translatedName().compare(right.translatedName(), Qt::CaseInsensitive); + return comparison < 0; + }); + + // load themes + + // do lookup in standard paths, if not disabled +#ifndef NO_STANDARD_PATHS + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/themes"), QStandardPaths::LocateDirectory)) + loadThemeFolder(dir); +#endif + + // default resources are always used + loadThemeFolder(QStringLiteral(":/org.kde.syntax-highlighting/themes")); + + // user given extra paths + foreach (const auto &path, m_customSearchPaths) + loadThemeFolder(path + QStringLiteral("/themes")); +} + +void RepositoryPrivate::loadSyntaxFolder(Repository *repo, const QString &path) +{ + if (loadSyntaxFolderFromIndex(repo, path)) + return; + + QDirIterator it(path, QStringList() << QLatin1String("*.xml"), QDir::Files); + while (it.hasNext()) { + Definition def; + auto defData = DefinitionData::get(def); + defData->repo = repo; + if (defData->loadMetaData(it.next())) + addDefinition(def); + } +} + +bool RepositoryPrivate::loadSyntaxFolderFromIndex(Repository *repo, const QString &path) +{ + QFile indexFile(path + QLatin1String("/index.katesyntax")); + if (!indexFile.open(QFile::ReadOnly)) + return false; + + const auto indexDoc(QJsonDocument::fromBinaryData(indexFile.readAll())); + const auto index = indexDoc.object(); + for (auto it = index.begin(); it != index.end(); ++it) { + if (!it.value().isObject()) + continue; + const auto fileName = QString(path + QLatin1Char('/') + it.key()); + const auto defMap = it.value().toObject(); + Definition def; + auto defData = DefinitionData::get(def); + defData->repo = repo; + if (defData->loadMetaData(fileName, defMap)) + addDefinition(def); + } + return true; +} + +void RepositoryPrivate::addDefinition(const Definition &def) +{ + const auto it = m_defs.constFind(def.name()); + if (it == m_defs.constEnd()) { + m_defs.insert(def.name(), def); + return; + } + + if (it.value().version() >= def.version()) + return; + m_defs.insert(def.name(), def); +} + +void RepositoryPrivate::loadThemeFolder(const QString &path) +{ + QDirIterator it(path, QStringList() << QLatin1String("*.theme"), QDir::Files); + while (it.hasNext()) { + auto themeData = std::unique_ptr<ThemeData>(new ThemeData); + if (themeData->load(it.next())) + addTheme(Theme(themeData.release())); + } +} + +static int themeRevision(const Theme &theme) +{ + auto data = ThemeData::get(theme); + return data->revision(); +} + +void RepositoryPrivate::addTheme(const Theme &theme) +{ + const auto it = std::lower_bound(m_themes.begin(), m_themes.end(), theme, [](const Theme &lhs, const Theme &rhs) { + return lhs.name() < rhs.name(); + }); + if (it == m_themes.end() || (*it).name() != theme.name()) { + m_themes.insert(it, theme); + return; + } + if (themeRevision(*it) < themeRevision(theme)) + *it = theme; +} + +quint16 RepositoryPrivate::foldingRegionId(const QString &defName, const QString &foldName) +{ + const auto it = m_foldingRegionIds.constFind(qMakePair(defName, foldName)); + if (it != m_foldingRegionIds.constEnd()) + return it.value(); + m_foldingRegionIds.insert(qMakePair(defName, foldName), ++m_foldingRegionId); + return m_foldingRegionId; +} + +quint16 RepositoryPrivate::nextFormatId() +{ + Q_ASSERT(m_formatId < std::numeric_limits<quint16>::max()); + return ++m_formatId; +} + +void Repository::reload() +{ + qCDebug(Log) << "Reloading syntax definitions!"; + foreach (const auto &def, d->m_sortedDefs) + DefinitionData::get(def)->clear(); + d->m_defs.clear(); + d->m_sortedDefs.clear(); + + d->m_themes.clear(); + + d->m_foldingRegionId = 0; + d->m_foldingRegionIds.clear(); + + d->m_formatId = 0; + + d->load(this); +} + +void Repository::addCustomSearchPath(const QString &path) +{ + d->m_customSearchPaths.append(path); + reload(); +} + +QVector<QString> Repository::customSearchPaths() const +{ + return d->m_customSearchPaths; +} |