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 }