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

0001 /*
0002     SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "templateclassgenerator.h"
0008 #include "archivetemplateloader.h"
0009 #include <debug.h>
0010 
0011 #include <interfaces/iproject.h>
0012 #include "language/codegen/documentchangeset.h"
0013 #include "codedescription.h"
0014 #include "templaterenderer.h"
0015 #include "sourcefiletemplate.h"
0016 #include <duchain/declaration.h>
0017 #include <duchain/duchainlock.h>
0018 #include <duchain/persistentsymboltable.h>
0019 #include <duchain/types/structuretype.h>
0020 #include <project/projectmodel.h>
0021 
0022 #include <QFileInfo>
0023 
0024 #include <array>
0025 
0026 using namespace KDevelop;
0027 
0028 /// @param base String such as 'public QObject' or 'QObject'
0029 InheritanceDescription descriptionFromString(const QString& base)
0030 {
0031     QStringList splitBase = base.split(QLatin1Char(' '));
0032     QString identifier = splitBase.takeLast();
0033     QString inheritanceMode = splitBase.join(QLatin1Char(' '));
0034 
0035     InheritanceDescription desc;
0036     desc.baseType = identifier;
0037     desc.inheritanceMode = inheritanceMode;
0038     return desc;
0039 }
0040 
0041 class KDevelop::TemplateClassGeneratorPrivate
0042 {
0043 public:
0044     SourceFileTemplate fileTemplate;
0045     QUrl baseUrl;
0046     // changes state when rendering
0047     mutable TemplateRenderer renderer;
0048 
0049     QString name;
0050     QString identifier;
0051     QStringList namespaces;
0052     QString license;
0053 
0054     // lazily estimated
0055     mutable QHash<QString, QUrl> fileUrls;
0056     QHash<QString, KTextEditor::Cursor> filePositions;
0057     ClassDescription description;
0058 
0059     QList<DeclarationPointer> directBaseClasses;
0060     QList<DeclarationPointer> allBaseClasses;
0061 
0062     void fetchSuperClasses(const DeclarationPointer& declaration);
0063 };
0064 
0065 void TemplateClassGeneratorPrivate::fetchSuperClasses(const DeclarationPointer& declaration)
0066 {
0067     DUChainReadLocker lock;
0068 
0069     //Prevent duplicity
0070     if (allBaseClasses.contains(declaration)) {
0071         return;
0072     }
0073 
0074     allBaseClasses << declaration;
0075 
0076     DUContext* context = declaration->internalContext();
0077     if (context) {
0078         const auto importedParentContexts = context->importedParentContexts();
0079         for (const DUContext::Import& import : importedParentContexts) {
0080             if (DUContext* parentContext = import.context(context->topContext())) {
0081                 if (parentContext->type() == DUContext::Class) {
0082                     fetchSuperClasses(DeclarationPointer(parentContext->owner()));
0083                 }
0084             }
0085         }
0086     }
0087 }
0088 
0089 TemplateClassGenerator::TemplateClassGenerator(const QUrl& baseUrl)
0090     : d_ptr(new TemplateClassGeneratorPrivate)
0091 {
0092     Q_D(TemplateClassGenerator);
0093 
0094     Q_ASSERT(QFileInfo(baseUrl.toLocalFile()).isDir()); // assume folder
0095 
0096     d->baseUrl = baseUrl;
0097     d->renderer.setEmptyLinesPolicy(TemplateRenderer::TrimEmptyLines);
0098 }
0099 
0100 TemplateClassGenerator::~TemplateClassGenerator() = default;
0101 
0102 void TemplateClassGenerator::setTemplateDescription(const SourceFileTemplate& fileTemplate)
0103 {
0104     Q_D(TemplateClassGenerator);
0105 
0106     d->fileTemplate = fileTemplate;
0107     Q_ASSERT(fileTemplate.isValid());
0108 }
0109 
0110 DocumentChangeSet TemplateClassGenerator::generate()
0111 {
0112     Q_D(TemplateClassGenerator);
0113 
0114     return d->renderer.renderFileTemplate(d->fileTemplate, d->baseUrl, fileUrls());
0115 }
0116 
0117 QHash<QString, QString> TemplateClassGenerator::fileLabels() const
0118 {
0119     Q_D(const TemplateClassGenerator);
0120 
0121     Q_ASSERT(d->fileTemplate.isValid());
0122     QHash<QString, QString> labels;
0123 
0124     const auto outputFiles = d->fileTemplate.outputFiles();
0125     labels.reserve(outputFiles.size());
0126     for (const SourceFileTemplate::OutputFile& outputFile : outputFiles) {
0127         labels.insert(outputFile.identifier, outputFile.label);
0128     }
0129 
0130     return labels;
0131 }
0132 
0133 TemplateClassGenerator::UrlHash TemplateClassGenerator::fileUrls() const
0134 {
0135     Q_D(const TemplateClassGenerator);
0136 
0137     if (d->fileUrls.isEmpty()) {
0138         const auto outputFiles = d->fileTemplate.outputFiles();
0139         for (const SourceFileTemplate::OutputFile& outputFile : outputFiles) {
0140             QString outputName = d->renderer.render(outputFile.outputName, outputFile.identifier);
0141             QUrl url = d->baseUrl.resolved(QUrl(outputName));
0142             d->fileUrls.insert(outputFile.identifier, url);
0143         }
0144     }
0145 
0146     return d->fileUrls;
0147 }
0148 
0149 QUrl TemplateClassGenerator::baseUrl() const
0150 {
0151     Q_D(const TemplateClassGenerator);
0152 
0153     return d->baseUrl;
0154 }
0155 
0156 QUrl TemplateClassGenerator::fileUrl(const QString& outputFile) const
0157 {
0158     return fileUrls().value(outputFile);
0159 }
0160 
0161 void TemplateClassGenerator::setFileUrl(const QString& outputFile, const QUrl& url)
0162 {
0163     Q_D(TemplateClassGenerator);
0164 
0165     d->fileUrls.insert(outputFile, url);
0166     d->renderer.addVariable(QLatin1String("output_file_") + outputFile.toLower(), QDir(
0167                                 d->baseUrl.path()).relativeFilePath(url.path()));
0168     d->renderer.addVariable(QLatin1String("output_file_") + outputFile.toLower() + QLatin1String(
0169                                 "_absolute"), url.toLocalFile());
0170 }
0171 
0172 KTextEditor::Cursor TemplateClassGenerator::filePosition(const QString& outputFile) const
0173 {
0174     Q_D(const TemplateClassGenerator);
0175 
0176     return d->filePositions.value(outputFile);
0177 }
0178 
0179 void TemplateClassGenerator::setFilePosition(const QString& outputFile, const KTextEditor::Cursor& position)
0180 {
0181     Q_D(TemplateClassGenerator);
0182 
0183     d->filePositions.insert(outputFile, position);
0184 }
0185 
0186 void TemplateClassGenerator::addVariables(const QVariantHash& variables)
0187 {
0188     Q_D(TemplateClassGenerator);
0189 
0190     d->renderer.addVariables(variables);
0191 }
0192 
0193 QString TemplateClassGenerator::renderString(const QString& text) const
0194 {
0195     Q_D(const TemplateClassGenerator);
0196 
0197     return d->renderer.render(text);
0198 }
0199 
0200 SourceFileTemplate TemplateClassGenerator::sourceFileTemplate() const
0201 {
0202     Q_D(const TemplateClassGenerator);
0203 
0204     return d->fileTemplate;
0205 }
0206 
0207 TemplateRenderer* TemplateClassGenerator::renderer() const
0208 {
0209     Q_D(const TemplateClassGenerator);
0210 
0211     return &(d->renderer);
0212 }
0213 
0214 QString TemplateClassGenerator::name() const
0215 {
0216     Q_D(const TemplateClassGenerator);
0217 
0218     return d->name;
0219 }
0220 
0221 void TemplateClassGenerator::setName(const QString& newName)
0222 {
0223     Q_D(TemplateClassGenerator);
0224 
0225     d->name = newName;
0226     d->renderer.addVariable(QStringLiteral("name"), newName);
0227 }
0228 
0229 QString TemplateClassGenerator::identifier() const
0230 {
0231     return name();
0232 }
0233 
0234 void TemplateClassGenerator::setIdentifier(const QString& identifier)
0235 {
0236     Q_D(TemplateClassGenerator);
0237 
0238     d->renderer.addVariable(QStringLiteral("identifier"), identifier);
0239     const std::array<QString,5> separators {
0240         QStringLiteral("::"),
0241         QStringLiteral("."),
0242         QStringLiteral(":"),
0243         QStringLiteral("\\"),
0244         QStringLiteral("/"),
0245     };
0246     QStringList ns;
0247     for (const QString& separator : separators) {
0248         ns = identifier.split(separator);
0249         if (ns.size() > 1) {
0250             break;
0251         }
0252     }
0253 
0254     setName(ns.takeLast());
0255     setNamespaces(ns);
0256 }
0257 
0258 QStringList TemplateClassGenerator::namespaces() const
0259 {
0260     Q_D(const TemplateClassGenerator);
0261 
0262     return d->namespaces;
0263 }
0264 
0265 void TemplateClassGenerator::setNamespaces(const QStringList& namespaces)
0266 {
0267     Q_D(TemplateClassGenerator);
0268 
0269     d->namespaces = namespaces;
0270     d->renderer.addVariable(QStringLiteral("namespaces"), namespaces);
0271 }
0272 
0273 /// Specify license for this class
0274 void TemplateClassGenerator::setLicense(const QString& license)
0275 {
0276     Q_D(TemplateClassGenerator);
0277 
0278     qCDebug(LANGUAGE) << "New Class: " << d->name << "Set license: " << d->license;
0279     d->license = license;
0280     d->renderer.addVariable(QStringLiteral("license"), license);
0281 }
0282 
0283 /// Get the license specified for this classes
0284 QString TemplateClassGenerator::license() const
0285 {
0286     Q_D(const TemplateClassGenerator);
0287 
0288     return d->license;
0289 }
0290 
0291 void TemplateClassGenerator::setDescription(const ClassDescription& description)
0292 {
0293     Q_D(TemplateClassGenerator);
0294 
0295     d->description = description;
0296 
0297     QVariantHash variables;
0298     variables[QStringLiteral("description")] = QVariant::fromValue(description);
0299     variables[QStringLiteral("members")] = CodeDescription::toVariantList(description.members);
0300     variables[QStringLiteral("functions")] = CodeDescription::toVariantList(description.methods);
0301     variables[QStringLiteral("base_classes")] = CodeDescription::toVariantList(description.baseClasses);
0302     d->renderer.addVariables(variables);
0303 }
0304 
0305 ClassDescription TemplateClassGenerator::description() const
0306 {
0307     Q_D(const TemplateClassGenerator);
0308 
0309     return d->description;
0310 }
0311 
0312 void TemplateClassGenerator::addBaseClass(const QString& base)
0313 {
0314     Q_D(TemplateClassGenerator);
0315 
0316     const InheritanceDescription desc = descriptionFromString(base);
0317 
0318     ClassDescription cd = description();
0319     cd.baseClasses << desc;
0320     setDescription(cd);
0321 
0322     //Search for all super classes
0323     auto visitor = [&](const IndexedDeclaration& indexedDeclaration) {
0324         auto declaration = DeclarationPointer(indexedDeclaration.declaration());
0325         if (!declaration || declaration->isForwardDeclaration()) {
0326             return PersistentSymbolTable::VisitorState::Continue;
0327         }
0328 
0329         // Check if it's a class/struct/etc
0330         if (declaration->type<StructureType>()) {
0331             d->fetchSuperClasses(declaration);
0332             d->directBaseClasses << declaration;
0333             return PersistentSymbolTable::VisitorState::Break;
0334         }
0335         return PersistentSymbolTable::VisitorState::Continue;
0336     };
0337     const auto id = IndexedQualifiedIdentifier(QualifiedIdentifier(desc.baseType));
0338 
0339     DUChainReadLocker lock;
0340     PersistentSymbolTable::self().visitDeclarations(id, visitor);
0341 }
0342 
0343 void TemplateClassGenerator::setBaseClasses(const QList<QString>& bases)
0344 {
0345     Q_D(TemplateClassGenerator);
0346 
0347     // clear
0348     ClassDescription cd = description();
0349     cd.baseClasses.clear();
0350     setDescription(cd);
0351 
0352     d->directBaseClasses.clear();
0353     d->allBaseClasses.clear();
0354 
0355     // add all bases
0356     for (const QString& base : bases) {
0357         addBaseClass(base);
0358     }
0359 }
0360 
0361 QList<DeclarationPointer> TemplateClassGenerator::directBaseClasses() const
0362 {
0363     Q_D(const TemplateClassGenerator);
0364 
0365     return d->directBaseClasses;
0366 }
0367 
0368 QList<DeclarationPointer> TemplateClassGenerator::allBaseClasses() const
0369 {
0370     Q_D(const TemplateClassGenerator);
0371 
0372     return d->allBaseClasses;
0373 }