kde-extraapps/kdevplatform/language/codegen/codegenerator.h
2015-07-26 14:23:17 +03:00

251 lines
7.2 KiB
C++

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_CODEGENERATOR_H
#define KDEVPLATFORM_CODEGENERATOR_H
#include "language/editor/documentrange.h"
#include "../duchain/indexedstring.h"
#include "../duchain/topducontext.h"
#include "../duchain/duchain.h"
#include "language/interfaces/iastcontainer.h"
namespace KDevelop
{
template<class AstNode >
class AstChangeSet;
class DUContext;
class DUChainChangeSet;
class DocumentChangeSet;
/**
* \short Base class for generic code generators
*
* CodeGeneratorBase provides an api for a step-by-step process to
* create and/or refactor code.
*
* This class should be used as a superclass only when du-chain level
* changes are made, since this level knows nothing about the
* language-specific AST. For more complex changes that require knowledge
* about the language AST, use CodeGenerator
*
* \see Refactoring
* \see CodeGenerator
* \author Hamish Rodda <rodda@kde.org>
*/
class KDEVPLATFORMLANGUAGE_EXPORT CodeGeneratorBase
{
public:
CodeGeneratorBase();
virtual ~CodeGeneratorBase();
enum State {
Precondition,
UserInput,
Processing,
Review,
Finished
};
/**
* Check whether the preconditions of this generation are met at the given \a context and
* \a position.
* \returns true if conditions are met and the generator can progress, otherwise false if
* the conditions are not met. Use setErrorText to specify the nature of the Error.
*/
virtual bool checkPreconditions(DUContext* context, const DocumentRange& position) = 0;
/**
* Gather information required from the user for this generator.
*
* \returns true if all of the information is retrieved, otherwise false, Use setErrorText
* to specify the nature of the Error.
*/
virtual bool gatherInformation() = 0;
/**
* Do final condition checking and perform the code generation.
*
* \returns true if code generation was successful, false otherwise. Use setErrorText to
* specify the nature of the Error.
*/
virtual bool process() = 0;
const QString & errorText() const;
// Implementation from kJob
bool execute();
/**
* @brief Indicates that this generation should not expect interaction with the user/
* Most probable scenarios are: Testing, and a generator that is being used by another one
* @param context If not NULL, the custom context to use, instead of user selection
* @param position If not NULL, the cursom range to use instead of user selection
* @note If this function is called, then gather information will not be called.
* Derived classes should provide an alternative way of setting up the generator.
*/
void autoGenerate(DUContext * context, const DocumentRange * range);
/**
* \return The Document Change set to add a single Change, it is more addicient than creating a local DocumentChangeSet and merging it
*/
DocumentChangeSet & documentChangeSet();
protected:
/**
* Generate text edits from duchain change set.
* This generator now owns the changeset, and will delete it.
*
* You may call this method multiple times to edit different files.
*/
void addChangeSet(DUChainChangeSet* duChainChange);
void addChangeSet(DocumentChangeSet & docChangeSet);
/**
* Accessor for KJob's KJob::setErrorText.
*/
void setErrorText(const QString & error);
/**
* Inform the derived class if this generation is being performed without user interaction
*/
bool autoGeneration() const;
/**
* Clean up all the change sets that this generator is in charge of
*/
void clearChangeSets();
private:
class CodeGeneratorPrivate * const d;
bool displayChanges();
};
/**
* \brief Base class for Ast aware code generators
*
* This class provides convenience for adding AstChangeSet, storing
* the IAstContainer from the TopDUContext, and in general managing
* Code generators that manipulate the AST
*
* \see CodeGeneratorBase
* \author Ramón Zarazúa <killerfox512+kde@gmail.com>
*/
template <typename AstContainer>
class CodeGenerator : public CodeGeneratorBase
{
public:
~CodeGenerator()
{
clearChangeSets();
}
protected:
/// Convenience definition of the TopAstNode that is contained by this AstContainer
typedef typename AstContainer::TopAstNode TopAstNode;
typedef AstChangeSet<TopAstNode> LanguageChangeSet;
/**
* Query an AST of a particular file
*/
TopAstNode * ast(const IndexedString & file)
{
return astContainer(file)->topAstNode();
}
TopAstNode * ast(const TopDUContext & context)
{
return astContainer(context)->topAstNode();
}
typename AstContainer::Ptr astContainer(const IndexedString & file)
{
if(!m_AstContainers.contains(file))
{
kDebug() << "Ast requested for: " << file.str();
TopDUContext * context = DUChain::self()->waitForUpdate(file, KDevelop::TopDUContext::AST).data();
Q_ASSERT(context);
m_AstContainers[file] = AstContainer::Ptr::template staticCast<IAstContainer>( context->ast() );
}
return m_AstContainers[file];
}
typename AstContainer::Ptr astContainer(const TopDUContext & context)
{
return astContainer(context.url());
}
/**
* Generate text edits from duchain / ast change set.
*
* You may call this method multiple times to edit different files.
*/
void addChangeSet(DUChainChangeSet * duChainChange)
{
CodeGeneratorBase::addChangeSet(duChainChange);
}
void addChangeSet(DocumentChangeSet & doc)
{
CodeGeneratorBase::addChangeSet(doc);
}
/**
* Generate text edits from duchain / ast change set.
*
* You may call this method multiple times to edit different files.
*/
void addChangeSet(LanguageChangeSet * astChange);
void clearChangeSets()
{
CodeGeneratorBase::clearChangeSets();
}
/**
* Inform the derived class if this generation is being performed without user interaction
*/
bool autoGeneration() const
{
return CodeGeneratorBase::autoGeneration();
}
/**
* Accessor for KJob's KJob::setErrorText.
*/
void setErrorText(const QString & error)
{
CodeGeneratorBase::setErrorText(error);
}
private:
typedef QMap<IndexedString, typename AstContainer::Ptr> AstContainerMap;
AstContainerMap m_AstContainers;
};
}
#endif // KDEVPLATFORM_CODEGENERATOR_H