File indexing completed on 2024-05-12 04:37:41

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de>
0003     SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "staticassistantsmanager.h"
0009 #include <debug.h>
0010 
0011 #include <KTextEditor/Document>
0012 #include <KTextEditor/View>
0013 
0014 #include <interfaces/icore.h>
0015 #include <interfaces/idocumentcontroller.h>
0016 #include <interfaces/ilanguagecontroller.h>
0017 #include <interfaces/ilanguagesupport.h>
0018 
0019 #include <language/duchain/duchainlock.h>
0020 #include <language/duchain/duchain.h>
0021 #include <language/duchain/declaration.h>
0022 #include <language/duchain/duchainutils.h>
0023 
0024 #include <language/duchain/problem.h>
0025 #include <language/editor/documentrange.h>
0026 
0027 using namespace KDevelop;
0028 using namespace KTextEditor;
0029 
0030 class KDevelop::StaticAssistantsManagerPrivate
0031 {
0032 public:
0033     explicit StaticAssistantsManagerPrivate(StaticAssistantsManager* qq)
0034         : q(qq)
0035     { }
0036 
0037     void updateReady(const IndexedString& document, const KDevelop::ReferencedTopDUContext& topContext);
0038     void documentLoaded(KDevelop::IDocument*);
0039     void textInserted(KTextEditor::Document* document, const Cursor& cursor, const QString& text);
0040     void textRemoved(KTextEditor::Document* document, const Range& cursor, const QString& removedText);
0041 
0042     StaticAssistantsManager* q;
0043 
0044     QVector<StaticAssistant::Ptr> m_registeredAssistants;
0045 };
0046 
0047 StaticAssistantsManager::StaticAssistantsManager(QObject* parent)
0048     : QObject(parent)
0049     , d_ptr(new StaticAssistantsManagerPrivate(this))
0050 {
0051     Q_D(StaticAssistantsManager);
0052 
0053     connect(KDevelop::ICore::self()->documentController(),
0054             &IDocumentController::documentLoaded,
0055             this, [this](IDocument* document) {
0056         Q_D(StaticAssistantsManager);
0057         d->documentLoaded(document);
0058     });
0059     const auto documents = ICore::self()->documentController()->openDocuments();
0060     for (IDocument* document : documents) {
0061         d->documentLoaded(document);
0062     }
0063 
0064     connect(DUChain::self(), &DUChain::updateReady,
0065             this, &StaticAssistantsManager::notifyAssistants);
0066 }
0067 
0068 StaticAssistantsManager::~StaticAssistantsManager()
0069 {
0070 }
0071 
0072 void StaticAssistantsManager::registerAssistant(const StaticAssistant::Ptr& assistant)
0073 {
0074     Q_D(StaticAssistantsManager);
0075 
0076     if (d->m_registeredAssistants.contains(assistant))
0077         return;
0078 
0079     d->m_registeredAssistants << assistant;
0080 }
0081 
0082 void StaticAssistantsManager::unregisterAssistant(const StaticAssistant::Ptr& assistant)
0083 {
0084     Q_D(StaticAssistantsManager);
0085 
0086     d->m_registeredAssistants.removeOne(assistant);
0087 }
0088 
0089 QVector<StaticAssistant::Ptr> StaticAssistantsManager::registeredAssistants() const
0090 {
0091     Q_D(const StaticAssistantsManager);
0092 
0093     return d->m_registeredAssistants;
0094 }
0095 
0096 void StaticAssistantsManagerPrivate::documentLoaded(IDocument* document)
0097 {
0098     if (document->textDocument()) {
0099         auto doc = document->textDocument();
0100         QObject::connect(doc, &KTextEditor::Document::textInserted, q,
0101                          [&](KTextEditor::Document* doc, const Cursor& cursor, const QString& text) {
0102             textInserted(doc, cursor, text);
0103         });
0104         QObject::connect(doc, &KTextEditor::Document::textRemoved, q,
0105                          [&](KTextEditor::Document* doc, const Range& range, const QString& removedText) {
0106             textRemoved(doc, range, removedText);
0107         });
0108     }
0109 }
0110 
0111 void StaticAssistantsManagerPrivate::textInserted(Document* doc, const Cursor& cursor, const QString& text)
0112 {
0113     auto changed = false;
0114     for (auto& assistant : qAsConst(m_registeredAssistants)) {
0115         auto range = Range(cursor, cursor + Cursor(0, text.size()));
0116         auto wasUseful = assistant->isUseful();
0117         assistant->textChanged(doc, range, {});
0118         if (wasUseful != assistant->isUseful()) {
0119             changed = true;
0120         }
0121     }
0122 
0123     if (changed) {
0124         Q_EMIT q->problemsChanged(IndexedString(doc->url()));
0125     }
0126 }
0127 
0128 void StaticAssistantsManagerPrivate::textRemoved(Document* doc, const Range& range,
0129                                                  const QString& removedText)
0130 {
0131     auto changed = false;
0132     for (auto& assistant : qAsConst(m_registeredAssistants)) {
0133         auto wasUseful = assistant->isUseful();
0134         assistant->textChanged(doc, range, removedText);
0135         if (wasUseful != assistant->isUseful()) {
0136             changed = true;
0137         }
0138     }
0139 
0140     if (changed) {
0141         Q_EMIT q->problemsChanged(IndexedString(doc->url()));
0142     }
0143 }
0144 
0145 void StaticAssistantsManager::notifyAssistants(const IndexedString& url,
0146                                                const KDevelop::ReferencedTopDUContext& context)
0147 {
0148     Q_D(StaticAssistantsManager);
0149 
0150     for (auto& assistant : qAsConst(d->m_registeredAssistants)) {
0151         assistant->updateReady(url, context);
0152     }
0153 }
0154 
0155 QVector<KDevelop::Problem::Ptr> KDevelop::StaticAssistantsManager::problemsForContext(
0156     const KDevelop::ReferencedTopDUContext& top) const
0157 {
0158     Q_D(const StaticAssistantsManager);
0159 
0160     View* view = ICore::self()->documentController()->activeTextDocumentView();
0161     if (!view || !top || IndexedString(view->document()->url()) != top->url()) {
0162         return {};
0163     }
0164 
0165     auto doc = top->url();
0166     auto language = ICore::self()->languageController()->languagesForUrl(doc.toUrl()).value(0);
0167     if (!language) {
0168         return {};
0169     }
0170 
0171     auto ret = QVector<KDevelop::Problem::Ptr>();
0172     qCDebug(LANGUAGE) << "Trying to find assistants for language" << language->name();
0173     for (const auto& assistant : qAsConst(d->m_registeredAssistants)) {
0174         if (assistant->supportedLanguage() != language)
0175             continue;
0176 
0177         if (assistant->isUseful()) {
0178             qCDebug(LANGUAGE) << "assistant is now useful:" << assistant.data();
0179 
0180             auto p = new KDevelop::StaticAssistantProblem();
0181             auto range = assistant->displayRange();
0182             qCDebug(LANGUAGE) << "range:" << range;
0183             p->setFinalLocation(DocumentRange(doc, range));
0184             p->setSource(KDevelop::IProblem::SemanticAnalysis);
0185             p->setSeverity(KDevelop::IProblem::Warning);
0186             p->setDescription(assistant->title());
0187             p->setSolutionAssistant(IAssistant::Ptr(assistant.data()));
0188 
0189             ret.append(KDevelop::Problem::Ptr(p));
0190         }
0191     }
0192 
0193     return ret;
0194 }
0195 
0196 #include "moc_staticassistantsmanager.cpp"