File indexing completed on 2024-05-12 05:52:03

0001 /*
0002     SPDX-FileCopyrightText: 2019 Mark Nauwelaerts <mark.nauwelaerts@gmail.com>
0003     SPDX-FileCopyrightText: 2022 Waqar Ahmed <waqar.17a@gmail.com>
0004     SPDX-License-Identifier: MIT
0005 */
0006 #pragma once
0007 
0008 #include "ktexteditor_utils.h"
0009 
0010 #include <QJsonArray>
0011 #include <QJsonObject>
0012 #include <QPointer>
0013 #include <QRegularExpression>
0014 #include <QStandardItem>
0015 
0016 #include <KLocalizedString>
0017 #include <KTextEditor/Document>
0018 
0019 // helper data that holds diagnostics suppressions
0020 class DiagnosticSuppression
0021 {
0022     struct Suppression {
0023         QRegularExpression diag, code;
0024     };
0025     std::vector<Suppression> m_suppressions;
0026     QPointer<KTextEditor::Document> m_document;
0027 
0028 public:
0029     // construct from configuration
0030     DiagnosticSuppression(KTextEditor::Document *doc, const std::vector<QJsonObject> &serverConfigs, const std::vector<QString> &sessionSuppressions)
0031         : m_document(doc)
0032     {
0033         // check regexp and report
0034         auto checkRegExp = [](const QRegularExpression &regExp) {
0035             auto valid = regExp.isValid();
0036             if (!valid) {
0037                 auto error = regExp.errorString();
0038                 auto offset = regExp.patternErrorOffset();
0039                 auto msg = i18nc("@info", "Error in regular expression: %1\noffset %2: %3", regExp.pattern(), offset, error);
0040                 Utils::showMessage(msg, {}, QStringLiteral("LSP Client"), MessageType::Error);
0041             }
0042             return valid;
0043         };
0044 
0045         Q_ASSERT(doc);
0046         const auto localPath = doc->url().toLocalFile();
0047         for (const auto &serverConfig : serverConfigs) {
0048             const auto supps = serverConfig.value(QStringLiteral("suppressions")).toObject();
0049             for (const auto &entry : supps) {
0050                 // should be (array) tuple (last element optional)
0051                 // [url regexp, message regexp, code regexp]
0052                 const auto patterns = entry.toArray();
0053                 if (patterns.size() >= 2) {
0054                     const auto urlRegExp = QRegularExpression(patterns.at(0).toString());
0055                     if (urlRegExp.isValid() && urlRegExp.match(localPath).hasMatch()) {
0056                         QRegularExpression diagRegExp, codeRegExp;
0057                         diagRegExp = QRegularExpression(patterns.at(1).toString());
0058                         if (patterns.size() >= 3) {
0059                             codeRegExp = QRegularExpression(patterns.at(2).toString());
0060                         }
0061                         if (checkRegExp(diagRegExp) && checkRegExp(codeRegExp)) {
0062                             m_suppressions.push_back({diagRegExp, codeRegExp});
0063                         }
0064                     }
0065                 }
0066             }
0067         }
0068 
0069         // also consider session suppressions
0070         for (const auto &entry : sessionSuppressions) {
0071             auto pattern = QRegularExpression::escape(entry);
0072             m_suppressions.push_back({QRegularExpression(pattern), {}});
0073         }
0074     }
0075 
0076     bool match(const QStandardItem &item) const
0077     {
0078         for (const auto &s : m_suppressions) {
0079             if (s.diag.match(item.text()).hasMatch()) {
0080                 // retrieve and check code text if we need to match the content as well
0081                 if (m_document && !s.code.pattern().isEmpty()) {
0082                     auto range = item.data(/*RangeData::RangeRole*/).value<KTextEditor::Range>();
0083                     auto code = m_document->text(range);
0084                     if (!s.code.match(code).hasMatch()) {
0085                         continue;
0086                     }
0087                 }
0088                 return true;
0089             }
0090         }
0091         return false;
0092     }
0093 
0094     KTextEditor::Document *document()
0095     {
0096         return m_document;
0097     }
0098 };