File indexing completed on 2024-04-28 04:39:08

0001 /*
0002     SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "filter.h"
0008 
0009 #include <KConfigGroup>
0010 
0011 #include <array>
0012 
0013 using namespace KDevelop;
0014 
0015 Filter::Filter()
0016     : targets(Files | Folders)
0017 {
0018 }
0019 
0020 Filter::Filter(const SerializedFilter& filter)
0021     : pattern(QString(), Qt::CaseSensitive, QRegExp::WildcardUnix)
0022     , targets(filter.targets)
0023     , type(filter.type)
0024 {
0025     QString pattern = filter.pattern;
0026     if (!filter.pattern.startsWith(QLatin1Char('/')) && !filter.pattern.startsWith(QLatin1Char('*'))) {
0027         // implicitly match against trailing relative path
0028         pattern.prepend(QLatin1String("*/"));
0029     }
0030     if (pattern.endsWith(QLatin1Char('/')) && targets != Filter::Files) {
0031         // implicitly match against folders
0032         targets = Filter::Folders;
0033         pattern.chop(1);
0034     }
0035     this->pattern.setPattern(pattern);
0036 }
0037 
0038 SerializedFilter::SerializedFilter()
0039     : targets(Filter::Files | Filter::Folders)
0040 
0041 {
0042 
0043 }
0044 
0045 SerializedFilter::SerializedFilter(const QString& pattern, Filter::Targets targets, Filter::Type type)
0046     : pattern(pattern)
0047     , targets(targets)
0048     , type(type)
0049 {
0050 
0051 }
0052 
0053 namespace KDevelop {
0054 
0055 SerializedFilters defaultFilters()
0056 {
0057     SerializedFilters ret;
0058     ret.reserve(41);
0059 
0060     // filter hidden files
0061     ret << SerializedFilter(QStringLiteral(".*"), Filter::Targets(Filter::Files | Filter::Folders));
0062     // but do show some with special meaning
0063 
0064     static const std::array<QString, 19> configFiles = {
0065         // Version control
0066         QStringLiteral(".gitignore"),
0067         QStringLiteral(".gitmodules"),
0068         // https://pre-commit.com/
0069         QStringLiteral(".pre-commit-config.yaml"),
0070 
0071         // CI config files
0072 
0073         // https://docs.gitlab.com/ee/ci/yaml/
0074         QStringLiteral(".gitlab-ci.yml"),
0075         // https://travis-ci.org/
0076         QStringLiteral(".travis.yml"),
0077 
0078         // Code quality configs
0079 
0080         //   https://github.com/c4urself/bump2version/
0081         QStringLiteral(".bumpversion.cfg"),
0082         //   https://clang.llvm.org/docs/ClangFormat.html
0083         QStringLiteral(".clang-format"),
0084         //   https://github.com/rust-lang/rust-clippy#configure-the-behavior-of-some-lints
0085         QStringLiteral(".clippy.toml"),
0086         //   https://github.com/codespell-project/codespell#using-a-config-file
0087         QStringLiteral(".codespellrc"),
0088         //   https://editorconfig.org/
0089         QStringLiteral(".editorconfig"),
0090         //   https://pycqa.github.io/isort/docs/configuration/config_files.html
0091         QStringLiteral(".isort.cfg"),
0092         //   https://mypy.readthedocs.io/en/stable/config_file.html
0093         QStringLiteral(".mypy.ini"),
0094         //   https://pep8.readthedocs.io
0095         QStringLiteral(".pep8"),
0096         //   https://prettier.io/
0097         QStringLiteral(".prettierignore"),
0098         QStringLiteral(".prettierrc*"),
0099         //   https://www.pydocstyle.org/en/stable/usage.html#configuration-files
0100         QStringLiteral(".pydocstyle*"),
0101         //   https://pylint.readthedocs.io/en/stable/user_guide/usage/run.html#command-line-options
0102         QStringLiteral(".pylintrc"),
0103         //   https://docs.readthedocs.io/en/stable/config-file/v2.html
0104         QStringLiteral(".readthedocs.y*ml"),
0105         //   https://yamllint.readthedocs.io/en/stable/configuration.html#configuration
0106         QStringLiteral(".yamllint*"),
0107     };
0108     for (const QString& file : configFiles) {
0109         ret << SerializedFilter(file, Filter::Files, Filter::Inclusive);
0110     }
0111 
0112     static const std::array<QString, 1> configFolders = {
0113         // CI config folders
0114 
0115         // https://circleci.com/docs/
0116         QStringLiteral(".circleci"),
0117     };
0118     for (const QString& folder : configFolders) {
0119         ret << SerializedFilter(folder, Filter::Folders, Filter::Inclusive);
0120     }
0121 
0122     // common vcs folders which we want to hide
0123     static const std::array<QString, 9> invalidFolders = {
0124         QStringLiteral(".git"), QStringLiteral("CVS"), QStringLiteral(".svn"), QStringLiteral("_svn"),
0125         QStringLiteral("SCCS"), QStringLiteral("_darcs"), QStringLiteral(".hg"), QStringLiteral(".bzr"), QStringLiteral("__pycache__")
0126     };
0127     for (const QString& folder : invalidFolders) {
0128         ret << SerializedFilter(folder, Filter::Folders);
0129     }
0130 
0131     // common files which we want to hide
0132     static const std::array<QString, 20> filePatterns = {
0133         // binary files (Unix)
0134         QStringLiteral("*.o"), QStringLiteral("*.a"), QStringLiteral("*.so"), QStringLiteral("*.so.*"),
0135         // binary files (Windows)
0136         QStringLiteral("*.obj"), QStringLiteral("*.lib"), QStringLiteral("*.dll"), QStringLiteral("*.exp"), QStringLiteral("*.pdb"),
0137         // generated files
0138         QStringLiteral("moc_*.cpp"), QStringLiteral("*.moc"), QStringLiteral("ui_*.h"), QStringLiteral("*.qmlc"), QStringLiteral("qrc_*.cpp"),
0139         // backup files
0140         QStringLiteral("*~"), QStringLiteral("*.orig"), QStringLiteral(".*.kate-swp"), QStringLiteral(".*.swp"),
0141         // python cache and object files
0142         QStringLiteral("*.pyc"), QStringLiteral("*.pyo")
0143     };
0144     for (const QString& filePattern : filePatterns) {
0145         ret << SerializedFilter(filePattern, Filter::Files);
0146     }
0147 
0148     return ret;
0149 }
0150 
0151 SerializedFilters readFilters(const KSharedConfigPtr& config)
0152 {
0153     if (!config->hasGroup("Filters")) {
0154         return defaultFilters();
0155     }
0156     const KConfigGroup& group = config->group("Filters");
0157     const int size = group.readEntry("size", -1);
0158     if (size == -1) {
0159         // fallback
0160         return defaultFilters();
0161     }
0162 
0163     SerializedFilters filters;
0164     filters.reserve(size);
0165     for (int i = 0; i < size; ++i) {
0166         const QByteArray subGroup = QByteArray::number(i);
0167         if (!group.hasGroup(subGroup)) {
0168             continue;
0169         }
0170         const KConfigGroup& subConfig = group.group(subGroup);
0171         const QString pattern = subConfig.readEntry("pattern", QString());
0172         Filter::Targets targets(subConfig.readEntry("targets", 0));
0173         Filter::Type type = static_cast<Filter::Type>(subConfig.readEntry("inclusive", 0));
0174         filters << SerializedFilter(pattern, targets, type);
0175     }
0176 
0177     return filters;
0178 }
0179 
0180 void writeFilters(const SerializedFilters& filters, KSharedConfigPtr config)
0181 {
0182     // clear existing
0183     config->deleteGroup("Filters");
0184 
0185     // write new
0186     KConfigGroup group = config->group("Filters");
0187     group.writeEntry("size", filters.size());
0188     int i = 0;
0189     for (const SerializedFilter& filter : filters) {
0190         KConfigGroup subGroup = group.group(QByteArray::number(i++));
0191         subGroup.writeEntry("pattern", filter.pattern);
0192         subGroup.writeEntry("targets", static_cast<int>(filter.targets));
0193         subGroup.writeEntry("inclusive", static_cast<int>(filter.type));
0194     }
0195     config->sync();
0196 }
0197 
0198 Filters deserialize(const SerializedFilters& filters)
0199 {
0200     Filters ret;
0201     ret.reserve(filters.size());
0202     for (const SerializedFilter& filter : filters) {
0203         ret << Filter(filter);
0204     }
0205     return ret;
0206 }
0207 
0208 }