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