File indexing completed on 2024-05-12 04:37:47
0001 /* 0002 SPDX-License-Identifier: LGPL-2.0-only 0003 */ 0004 0005 #ifndef KDEVPLATFORM_CODEGENERATOR_H 0006 #define KDEVPLATFORM_CODEGENERATOR_H 0007 0008 #include <serialization/indexedstring.h> 0009 #include "../duchain/topducontext.h" 0010 #include "../duchain/duchain.h" 0011 0012 #include "language/interfaces/iastcontainer.h" 0013 #include <debug.h> 0014 0015 namespace KDevelop { 0016 template <class AstNode> 0017 class AstChangeSet; 0018 0019 class DUContext; 0020 class DUChainChangeSet; 0021 class DocumentChangeSet; 0022 class DocumentRange; 0023 class CodeGeneratorBasePrivate; 0024 0025 /** 0026 * \short Base class for generic code generators 0027 * 0028 * CodeGeneratorBase provides an api for a step-by-step process to 0029 * create and/or refactor code. 0030 * 0031 * This class should be used as a superclass only when du-chain level 0032 * changes are made, since this level knows nothing about the 0033 * language-specific AST. For more complex changes that require knowledge 0034 * about the language AST, use CodeGenerator 0035 * 0036 * \see Refactoring 0037 * \see CodeGenerator 0038 * \author Hamish Rodda <rodda@kde.org> 0039 */ 0040 class KDEVPLATFORMLANGUAGE_EXPORT CodeGeneratorBase 0041 { 0042 public: 0043 CodeGeneratorBase(); 0044 virtual ~CodeGeneratorBase(); 0045 0046 enum State { 0047 Precondition, 0048 UserInput, 0049 Processing, 0050 Review, 0051 Finished 0052 }; 0053 0054 /** 0055 * Check whether the preconditions of this generation are met at the given \a context and 0056 * \a position. 0057 * \returns true if conditions are met and the generator can progress, otherwise false if 0058 * the conditions are not met. Use setErrorText to specify the nature of the Error. 0059 */ 0060 virtual bool checkPreconditions(DUContext* context, const DocumentRange& position) = 0; 0061 0062 /** 0063 * Gather information required from the user for this generator. 0064 * 0065 * \returns true if all of the information is retrieved, otherwise false, Use setErrorText 0066 * to specify the nature of the Error. 0067 */ 0068 virtual bool gatherInformation() = 0; 0069 0070 /** 0071 * Do final condition checking and perform the code generation. 0072 * 0073 * \returns true if code generation was successful, false otherwise. Use setErrorText to 0074 * specify the nature of the Error. 0075 */ 0076 virtual bool process() = 0; 0077 0078 const QString& errorText() const; 0079 0080 // Implementation from kJob 0081 bool execute(); 0082 0083 /** 0084 * @brief Indicates that this generation should not expect interaction with the user/ 0085 * Most probable scenarios are: Testing, and a generator that is being used by another one 0086 * @param context If not NULL, the custom context to use, instead of user selection 0087 * @param range If not NULL, the cursom range to use instead of user selection 0088 * @note If this function is called, then gather information will not be called. 0089 * Derived classes should provide an alternative way of setting up the generator. 0090 */ 0091 void autoGenerate(DUContext* context, const DocumentRange* range); 0092 0093 /** 0094 * \return The Document Change set to add a single Change, it is more addicient than creating a local DocumentChangeSet and merging it 0095 */ 0096 DocumentChangeSet& documentChangeSet(); 0097 0098 protected: 0099 0100 /** 0101 * Generate text edits from duchain change set. 0102 * This generator now owns the changeset, and will delete it. 0103 * 0104 * You may call this method multiple times to edit different files. 0105 */ 0106 void addChangeSet(DUChainChangeSet* duChainChange); 0107 0108 void addChangeSet(DocumentChangeSet& docChangeSet); 0109 0110 /** 0111 * Accessor for KJob's KJob::setErrorText. 0112 */ 0113 void setErrorText(const QString& error); 0114 0115 /** 0116 * Inform the derived class if this generation is being performed without user interaction 0117 */ 0118 bool autoGeneration() const; 0119 0120 /** 0121 * Clean up all the change sets that this generator is in charge of 0122 */ 0123 void clearChangeSets(); 0124 0125 private: 0126 bool displayChanges(); 0127 0128 private: 0129 const QScopedPointer<class CodeGeneratorBasePrivate> d_ptr; 0130 Q_DECLARE_PRIVATE(CodeGeneratorBase) 0131 }; 0132 0133 /** 0134 * \brief Base class for Ast aware code generators 0135 * 0136 * This class provides convenience for adding AstChangeSet, storing 0137 * the IAstContainer from the TopDUContext, and in general managing 0138 * Code generators that manipulate the AST 0139 * 0140 * \see CodeGeneratorBase 0141 * \author Ramón Zarazúa <killerfox512+kde@gmail.com> 0142 */ 0143 template <typename AstContainer> 0144 class CodeGenerator 0145 : public CodeGeneratorBase 0146 { 0147 public: 0148 ~CodeGenerator() 0149 { 0150 clearChangeSets(); 0151 } 0152 0153 protected: 0154 0155 /// Convenience definition of the TopAstNode that is contained by this AstContainer 0156 using TopAstNode = typename AstContainer::TopAstNode ; 0157 using LanguageChangeSet = AstChangeSet<TopAstNode>; 0158 0159 /** 0160 * Query an AST of a particular file 0161 */ 0162 TopAstNode* ast(const IndexedString& file) 0163 { 0164 return astContainer(file)->topAstNode(); 0165 } 0166 0167 TopAstNode* ast(const TopDUContext& context) 0168 { 0169 return astContainer(context)->topAstNode(); 0170 } 0171 0172 typename AstContainer::Ptr astContainer(const IndexedString& file) 0173 { 0174 if (!m_AstContainers.contains(file)) { 0175 qCDebug(LANGUAGE) << "Ast requested for: " << file.str(); 0176 0177 TopDUContext* context = DUChain::self()->waitForUpdate(file, KDevelop::TopDUContext::AST).data(); 0178 0179 Q_ASSERT(context); 0180 m_AstContainers[file] = AstContainer::Ptr::template staticCast<IAstContainer>(context->ast()); 0181 } 0182 0183 return m_AstContainers[file]; 0184 } 0185 0186 typename AstContainer::Ptr astContainer(const TopDUContext& context) 0187 { 0188 return astContainer(context.url()); 0189 } 0190 0191 /** 0192 * Generate text edits from duchain / ast change set. 0193 * 0194 * You may call this method multiple times to edit different files. 0195 */ 0196 void addChangeSet(DUChainChangeSet* duChainChange) 0197 { 0198 CodeGeneratorBase::addChangeSet(duChainChange); 0199 } 0200 0201 void addChangeSet(DocumentChangeSet& doc) 0202 { 0203 CodeGeneratorBase::addChangeSet(doc); 0204 } 0205 0206 /** 0207 * Generate text edits from duchain / ast change set. 0208 * 0209 * You may call this method multiple times to edit different files. 0210 */ 0211 void addChangeSet(LanguageChangeSet* astChange); 0212 0213 void clearChangeSets() 0214 { 0215 CodeGeneratorBase::clearChangeSets(); 0216 } 0217 0218 /** 0219 * Inform the derived class if this generation is being performed without user interaction 0220 */ 0221 bool autoGeneration() const 0222 { 0223 return CodeGeneratorBase::autoGeneration(); 0224 } 0225 0226 /** 0227 * Accessor for KJob's KJob::setErrorText. 0228 */ 0229 void setErrorText(const QString& error) 0230 { 0231 CodeGeneratorBase::setErrorText(error); 0232 } 0233 0234 private: 0235 using AstContainerMap = QMap<IndexedString, typename AstContainer::Ptr>; 0236 AstContainerMap m_AstContainers; 0237 }; 0238 } 0239 0240 #endif // KDEVPLATFORM_CODEGENERATOR_H