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

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 "kateprivate_export.h"
0009 
0010 #include "diagnostic_types.h"
0011 
0012 #include <QJsonObject>
0013 #include <QPainter>
0014 #include <QPointer>
0015 #include <QStandardItemModel>
0016 #include <QUrl>
0017 #include <QWidget>
0018 
0019 #include <KColorScheme>
0020 #include <KXMLGUIClient>
0021 
0022 #include <KTextEditor/Document>
0023 #include <KTextEditor/Range>
0024 
0025 class KConfigGroup;
0026 class SessionDiagnosticSuppressions;
0027 class KateMainWindow;
0028 class QSortFilterProxyModel;
0029 class KateTextHintProvider;
0030 
0031 namespace KTextEditor
0032 {
0033 class MainWindow;
0034 class Mark;
0035 class View;
0036 class MovingRange;
0037 }
0038 
0039 class KATE_PRIVATE_EXPORT DiagnosticsProvider : public QObject
0040 {
0041     Q_OBJECT
0042 public:
0043     explicit DiagnosticsProvider(KTextEditor::MainWindow *mainWindow, QObject *parent = nullptr);
0044 
0045     // Get suppressions
0046     // e.g json object
0047     /*
0048      * "suppressions": {
0049      *     "rulename": ["filename_regex", "message regexp", "code regexp"],
0050      *  }
0051      */
0052     virtual QJsonObject suppressions(KTextEditor::Document *) const
0053     {
0054         return {};
0055     }
0056 
0057     /**
0058      * If @p filterTo is a valid provider than DiagnosticView will
0059      * filter out all diagnostics that are not from @p filterTo.
0060      */
0061     void showDiagnosticsView(DiagnosticsProvider *filterTo = nullptr);
0062 
0063     /**
0064      * If @p filterTo is a valid provider, then DiagnosticView will
0065      * filter out all diagnostics that are not from @p filterTo.
0066      */
0067     void filterDiagnosticsViewTo(DiagnosticsProvider *filterTo);
0068 
0069     /**
0070      * Whether diagnostics of this provider should be automatically cleared
0071      * when a document is closed
0072      */
0073     void setPersistentDiagnostics(bool p)
0074     {
0075         m_persistentDiagnostics = p;
0076     }
0077 
0078     bool persistentDiagnostics() const
0079     {
0080         return m_persistentDiagnostics;
0081     }
0082 
0083     QString name;
0084 
0085 Q_SIGNALS:
0086     /// emitted by provider when diags are available
0087     void diagnosticsAdded(const FileDiagnostics &);
0088 
0089     /// emitted by provider
0090     /// DiagnosticView will remove diagnostics where provider is @p provider
0091     void requestClearDiagnostics(DiagnosticsProvider *provider);
0092 
0093     /// Request fixes for given diagnostic
0094     /// @p data must be passed back when fixes are sent back via fixesAvailable()
0095     /// emitted by DiagnosticView
0096     void requestFixes(const QUrl &, const Diagnostic &, const QVariant &data);
0097 
0098     /// emitted by provider when fixes are available
0099     void fixesAvailable(const QList<DiagnosticFix> &fixes, const QVariant &data);
0100 
0101     /// emitted by provider to clear suppressions
0102     /// (as some state that the previously provided ones depend on may have changed
0103     void requestClearSuppressions(DiagnosticsProvider *provider);
0104 
0105 private:
0106     friend class DiagnosticsView;
0107     class DiagnosticsView *diagnosticView;
0108     bool m_persistentDiagnostics = false;
0109 };
0110 
0111 class DiagTabOverlay : public QWidget
0112 {
0113 public:
0114     DiagTabOverlay(QWidget *parent)
0115         : QWidget(parent)
0116         , m_tabButton(parent)
0117     {
0118         if (!parent) {
0119             hide();
0120             return;
0121         }
0122         setAttribute(Qt::WA_TransparentForMouseEvents, true);
0123         setGeometry(parent->geometry());
0124         move({0, 0});
0125         show();
0126         raise();
0127     }
0128 
0129     void setActive(bool a)
0130     {
0131         if (m_tabButton && (m_active != a)) {
0132             m_active = a;
0133             if (m_tabButton->size() != size()) {
0134                 resize(m_tabButton->size());
0135             }
0136             update();
0137         }
0138     }
0139 
0140 protected:
0141     void paintEvent(QPaintEvent *) override
0142     {
0143         if (m_active) {
0144             QPainter p(this);
0145             p.setOpacity(0.25);
0146             p.setBrush(KColorScheme().foreground(KColorScheme::NeutralText));
0147             p.setPen(Qt::NoPen);
0148             p.drawRect(rect().adjusted(1, 1, -1, -1));
0149         }
0150     }
0151 
0152 private:
0153     bool m_active = false;
0154     QWidget *m_tabButton = nullptr;
0155 };
0156 
0157 class DiagnosticsView : public QWidget, public KXMLGUIClient
0158 {
0159     Q_OBJECT
0160     friend class ForwardingTextHintProvider;
0161 
0162 protected:
0163     explicit DiagnosticsView(QWidget *parent, KTextEditor::MainWindow *mainWindow);
0164 
0165 public:
0166     static DiagnosticsView *instance(KTextEditor::MainWindow *mainWindow);
0167     ~DiagnosticsView();
0168 
0169     void registerDiagnosticsProvider(DiagnosticsProvider *provider);
0170     void unregisterDiagnosticsProvider(DiagnosticsProvider *provider);
0171 
0172     void readSessionConfig(const KConfigGroup &config);
0173     void writeSessionConfig(KConfigGroup &config);
0174 
0175     void onTextHint(KTextEditor::View *view, const KTextEditor::Cursor &position) const;
0176 
0177     void showToolview(DiagnosticsProvider *filterTo = nullptr);
0178     void filterViewTo(DiagnosticsProvider *provider);
0179 
0180 protected:
0181     void showEvent(QShowEvent *e) override;
0182     void handleEsc(QEvent *e);
0183 
0184 private Q_SLOTS:
0185     void tabForToolViewAdded(QWidget *toolView, QWidget *tab);
0186 
0187 private:
0188     void onFixesAvailable(const QList<DiagnosticFix> &fixes, const QVariant &data);
0189     void showFixesInMenu(const QList<DiagnosticFix> &fixes);
0190     void quickFix();
0191     void moveDiagnosticsSelection(bool forward);
0192     void nextItem();
0193     void previousItem();
0194     void onDiagnosticsAdded(const FileDiagnostics &diagnostics);
0195     void clearDiagnosticsFromProvider(DiagnosticsProvider *provider)
0196     {
0197         clearDiagnosticsForStaleDocs({}, provider);
0198     }
0199     void clearDiagnosticsForStaleDocs(const QList<QString> &filesToKeep, DiagnosticsProvider *provider);
0200     void clearSuppressionsFromProvider(DiagnosticsProvider *provider);
0201     void onDocumentUrlChanged();
0202     void updateDiagnosticsState(struct DocumentDiagnosticItem *&topItem);
0203     void updateMarks(const std::vector<QUrl> &urls = {});
0204     void goToItemLocation(QModelIndex index);
0205 
0206     void onViewChanged(KTextEditor::View *v);
0207 
0208     void onDoubleClicked(const QModelIndex &index, bool quickFix = false);
0209 
0210     void addMarks(KTextEditor::Document *doc, QStandardItem *item);
0211     void addMarksRec(KTextEditor::Document *doc, QStandardItem *item);
0212     void addMarks(KTextEditor::Document *doc);
0213 
0214     Q_SLOT void clearAllMarks(KTextEditor::Document *doc);
0215     Q_SLOT void onMarkClicked(KTextEditor::Document *document, KTextEditor::Mark mark, bool &handled);
0216 
0217     bool syncDiagnostics(KTextEditor::Document *document, int line, bool allowTop, bool doShow);
0218     void updateDiagnosticsSuppression(struct DocumentDiagnosticItem *topItem, KTextEditor::Document *doc, bool force = false);
0219 
0220     void onContextMenuRequested(const QPoint &pos);
0221 
0222     void setupDiagnosticViewToolbar(class QVBoxLayout *mainLayout);
0223 
0224     int m_diagnosticsCount = 0;
0225     KTextEditor::MainWindow *const m_mainWindow;
0226     class QTreeView *const m_diagnosticsTree;
0227     class QToolButton *const m_clearButton;
0228     class QLineEdit *const m_filterLineEdit;
0229     class QComboBox *const m_providerCombo;
0230     class QToolButton *const m_errFilterBtn;
0231     class QToolButton *const m_warnFilterBtn;
0232     class KMessageWidget *const m_diagLimitReachedWarning;
0233 
0234     class ProviderListModel *m_providerModel;
0235 
0236     QStandardItemModel m_model;
0237     QSortFilterProxyModel *const m_proxy;
0238     std::vector<DiagnosticsProvider *> m_providers;
0239     std::unique_ptr<SessionDiagnosticSuppressions> m_sessionDiagnosticSuppressions;
0240 
0241     QHash<KTextEditor::Document *, QList<KTextEditor::MovingRange *>> m_diagnosticsRanges;
0242     // applied marks
0243     QSet<KTextEditor::Document *> m_diagnosticsMarks;
0244 
0245     QPointer<DiagTabOverlay> m_tabButtonOverlay;
0246 
0247     QMetaObject::Connection posChangedConnection;
0248     QTimer *const m_posChangedTimer;
0249     QTimer *const m_filterChangedTimer;
0250     QTimer *const m_urlChangedTimer;
0251     std::unique_ptr<KateTextHintProvider> m_textHintProvider;
0252     int m_diagnosticLimit = 0;
0253 };