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

0001 /*
0002     SPDX-FileCopyrightText: 2008 Hamish Rodda <rodda@kde.org>
0003     SPDX-FileCopyrightText: 2009 Ramon Zarazua <killerfox512 gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "codegenerator.h"
0009 
0010 #include "documentchangeset.h"
0011 #include "duchainchangeset.h"
0012 
0013 #include <KLocalizedString>
0014 
0015 #include <interfaces/icore.h>
0016 #include <interfaces/idocumentcontroller.h>
0017 
0018 #include <language/duchain/duchainlock.h>
0019 
0020 #include "applychangeswidget.h"
0021 #include <debug.h>
0022 
0023 namespace KDevelop {
0024 class CodeGeneratorBasePrivate
0025 {
0026 public:
0027 
0028     CodeGeneratorBasePrivate() : autoGen(false)
0029         , context(0) {}
0030 
0031     QMap<IndexedString, DUChainChangeSet*> duchainChanges;
0032     DocumentChangeSet documentChanges;
0033 
0034     bool autoGen;
0035     DUContext* context;
0036     DocumentRange range;
0037     QString error;
0038 };
0039 
0040 CodeGeneratorBase::CodeGeneratorBase()
0041     : d_ptr(new CodeGeneratorBasePrivate)
0042 {
0043 }
0044 
0045 CodeGeneratorBase::~CodeGeneratorBase()
0046 {
0047     clearChangeSets();
0048 }
0049 
0050 void CodeGeneratorBase::autoGenerate(DUContext* context, const KDevelop::DocumentRange* range)
0051 {
0052     Q_D(CodeGeneratorBase);
0053 
0054     d->autoGen = true;
0055     d->context = context;
0056     d->range = *range;
0057 }
0058 
0059 void CodeGeneratorBase::addChangeSet(DUChainChangeSet* duchainChange)
0060 {
0061     Q_D(CodeGeneratorBase);
0062 
0063     IndexedString file = duchainChange->topDuContext().data()->url();
0064 
0065     QMap<IndexedString, DUChainChangeSet*>::iterator it = d->duchainChanges.find(file);
0066 
0067     //if we already have an entry for this file, merge it
0068     if (it != d->duchainChanges.end()) {
0069         **it << *duchainChange;
0070         delete duchainChange;
0071     } else
0072         d->duchainChanges.insert(file, duchainChange);
0073 }
0074 
0075 void CodeGeneratorBase::addChangeSet(DocumentChangeSet& docChangeSet)
0076 {
0077     Q_D(CodeGeneratorBase);
0078 
0079     d->documentChanges << docChangeSet;
0080 }
0081 
0082 DocumentChangeSet& CodeGeneratorBase::documentChangeSet()
0083 {
0084     Q_D(CodeGeneratorBase);
0085 
0086     return d->documentChanges;
0087 }
0088 
0089 const QString& CodeGeneratorBase::errorText() const
0090 {
0091     Q_D(const CodeGeneratorBase);
0092 
0093     return d->error;
0094 }
0095 
0096 bool CodeGeneratorBase::autoGeneration() const
0097 {
0098     Q_D(const CodeGeneratorBase);
0099 
0100     return d->autoGen;
0101 }
0102 
0103 void CodeGeneratorBase::setErrorText(const QString& errorText)
0104 {
0105     Q_D(CodeGeneratorBase);
0106 
0107     d->error = errorText;
0108 }
0109 
0110 void CodeGeneratorBase::clearChangeSets()
0111 {
0112     Q_D(CodeGeneratorBase);
0113 
0114     qCDebug(LANGUAGE) << "Cleaning up all the changesets registered by the generator";
0115     qDeleteAll(d->duchainChanges);
0116 
0117     d->duchainChanges.clear();
0118 
0119     d->documentChanges = DocumentChangeSet();
0120 }
0121 
0122 bool CodeGeneratorBase::execute()
0123 {
0124     Q_D(CodeGeneratorBase);
0125 
0126     qCDebug(LANGUAGE) << "Checking Preconditions for the codegenerator";
0127 
0128     //Shouldn't there be a method in iDocument to get a DocumentRange as well?
0129 
0130     QUrl document;
0131     if (!d->autoGen) {
0132         if (!ICore::self()->documentController()->activeDocument()) {
0133             setErrorText(i18n("Could not find an open document"));
0134             return false;
0135         }
0136 
0137         document = ICore::self()->documentController()->activeDocument()->url();
0138 
0139         if (d->range.isEmpty()) {
0140             DUChainReadLocker lock(DUChain::lock());
0141             d->range = DocumentRange(document.url(),
0142                                      ICore::self()->documentController()->activeDocument()->textSelection());
0143         }
0144     }
0145 
0146     if (!d->context) {
0147         DUChainReadLocker lock(DUChain::lock());
0148         TopDUContext* documentChain = DUChain::self()->chainForDocument(document);
0149         if (!documentChain) {
0150             setErrorText(i18n("Could not find the chain for the selected document: %1", document.url()));
0151             return false;
0152         }
0153         d->context = documentChain->findContextIncluding(d->range);
0154 
0155         if (!d->context) {
0156             //Attempt to get the context again
0157             const QList<TopDUContext*> contexts = DUChain::self()->chainsForDocument(document);
0158             for (TopDUContext* top : contexts) {
0159                 qCDebug(LANGUAGE) << "Checking top context with range: " << top->range() << " for a context";
0160                 if ((d->context = top->findContextIncluding(d->range)))
0161                     break;
0162             }
0163         }
0164     }
0165 
0166     if (!d->context) {
0167         setErrorText(i18n("Error finding context for selection range"));
0168         return false;
0169     }
0170 
0171     if (!checkPreconditions(d->context, d->range)) {
0172         setErrorText(i18n("Error checking conditions to generate code: %1", errorText()));
0173         return false;
0174     }
0175 
0176     if (!d->autoGen) {
0177         qCDebug(LANGUAGE) << "Gathering user information for the codegenerator";
0178         if (!gatherInformation()) {
0179             setErrorText(i18n("Error Gathering user information: %1", errorText()));
0180             return false;
0181         }
0182     }
0183 
0184     qCDebug(LANGUAGE) << "Generating code";
0185     if (!process()) {
0186         setErrorText(i18n("Error generating code: %1", errorText()));
0187         return false;
0188     }
0189 
0190     if (!d->autoGen) {
0191         qCDebug(LANGUAGE) << "Submitting to the user for review";
0192         return displayChanges();
0193     }
0194 
0195     //If it is autogenerated, it shouldn't need to apply changes, instead return them to client that my be another generator
0196     DocumentChangeSet::ChangeResult result(true);
0197     if (!d->autoGen && !(result = d->documentChanges.applyAllChanges())) {
0198         setErrorText(result.m_failureReason);
0199         return false;
0200     }
0201 
0202     return true;
0203 }
0204 
0205 bool CodeGeneratorBase::displayChanges()
0206 {
0207     Q_D(CodeGeneratorBase);
0208 
0209     DocumentChangeSet::ChangeResult result = d->documentChanges.applyAllToTemp();
0210     if (!result) {
0211         setErrorText(result.m_failureReason);
0212         return false;
0213     }
0214 
0215     ApplyChangesWidget widget;
0216     //TODO: Find some good information to put
0217     widget.setInformation("Info?");
0218 
0219     QMap<IndexedString, IndexedString> temps = d->documentChanges.tempNamesForAll();
0220     for (QMap<IndexedString, IndexedString>::iterator it = temps.begin();
0221          it != temps.end(); ++it)
0222         widget.addDocuments(it.key(), it.value());
0223 
0224     if (widget.exec())
0225         return widget.applyAllChanges();
0226     else
0227         return false;
0228 }
0229 }