mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
344 lines
9.3 KiB
C++
344 lines
9.3 KiB
C++
/*
|
|
* This file is part of KDevelop
|
|
* Copyright 2012 Miha Čančula <miha@noughmad.eu>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "templaterenderer.h"
|
|
|
|
#include "documentchangeset.h"
|
|
#include "sourcefiletemplate.h"
|
|
#include "templateengine.h"
|
|
#include "templateengine_p.h"
|
|
#include "archivetemplateloader.h"
|
|
|
|
#include <language/editor/simplecursor.h>
|
|
#include <language/editor/simplerange.h>
|
|
#include <language/duchain/indexedstring.h>
|
|
|
|
#include <grantlee/context.h>
|
|
|
|
#include <KUrl>
|
|
#include <KDebug>
|
|
#include <KArchive>
|
|
|
|
#include <QFile>
|
|
|
|
using namespace Grantlee;
|
|
|
|
class NoEscapeStream : public OutputStream
|
|
{
|
|
public:
|
|
NoEscapeStream();
|
|
explicit NoEscapeStream (QTextStream* stream);
|
|
|
|
virtual QString escape (const QString& input) const;
|
|
virtual QSharedPointer< OutputStream > clone (QTextStream* stream) const;
|
|
};
|
|
|
|
NoEscapeStream::NoEscapeStream() : OutputStream()
|
|
{
|
|
|
|
}
|
|
|
|
NoEscapeStream::NoEscapeStream(QTextStream* stream) : OutputStream (stream)
|
|
{
|
|
|
|
}
|
|
|
|
QString NoEscapeStream::escape(const QString& input) const
|
|
{
|
|
return input;
|
|
}
|
|
|
|
QSharedPointer<OutputStream> NoEscapeStream::clone(QTextStream* stream) const
|
|
{
|
|
QSharedPointer<OutputStream> clonedStream = QSharedPointer<OutputStream>( new NoEscapeStream( stream ) );
|
|
return clonedStream;
|
|
}
|
|
|
|
using namespace KDevelop;
|
|
|
|
namespace KDevelop {
|
|
|
|
class TemplateRendererPrivate
|
|
{
|
|
public:
|
|
Engine* engine;
|
|
Grantlee::Context context;
|
|
TemplateRenderer::EmptyLinesPolicy emptyLinesPolicy;
|
|
QString errorString;
|
|
};
|
|
|
|
}
|
|
|
|
TemplateRenderer::TemplateRenderer()
|
|
: d(new TemplateRendererPrivate)
|
|
{
|
|
d->engine = &TemplateEngine::self()->d->engine;
|
|
d->emptyLinesPolicy = KeepEmptyLines;
|
|
}
|
|
|
|
TemplateRenderer::~TemplateRenderer()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void TemplateRenderer::addVariables(const QVariantHash& variables)
|
|
{
|
|
QVariantHash::const_iterator it = variables.constBegin();
|
|
QVariantHash::const_iterator end = variables.constEnd();
|
|
for (; it != end; ++it)
|
|
{
|
|
d->context.insert(it.key(), it.value());
|
|
}
|
|
}
|
|
|
|
void TemplateRenderer::addVariable(const QString& name, const QVariant& value)
|
|
{
|
|
d->context.insert(name, value);
|
|
}
|
|
|
|
QVariantHash TemplateRenderer::variables() const
|
|
{
|
|
return d->context.stackHash(0);
|
|
}
|
|
|
|
QString TemplateRenderer::render(const QString& content, const QString& name) const
|
|
{
|
|
#if WITH_SMART_TRIM
|
|
Template t = d->engine->newTemplate(content, name);
|
|
#else
|
|
/*
|
|
* This code re-implements the functionality of Grantlee's smart trim feature, which was added in 0.1.8
|
|
* It follows the description from http://www.grantlee.org/apidox/classGrantlee_1_1Engine.html#smart_trim
|
|
* and passes the unit tests.
|
|
*/
|
|
const QStringList lines = content.split('\n');
|
|
|
|
QStringList trimmedLines;
|
|
trimmedLines << QString();
|
|
|
|
for (QStringList::const_iterator it = lines.constBegin(); it != lines.constEnd(); ++it)
|
|
{
|
|
QString s = (*it);
|
|
const QString t = s.trimmed();
|
|
if ( (t.startsWith("{%") && t.endsWith("%}") && t.count('{') == 1 && t.count('}') == 1)
|
|
|| (t.startsWith("{{") && t.endsWith("}}") && t.count('{') == 2 && t.count('}') == 2)
|
|
|| (t.startsWith("{#") && t.endsWith("#}") && t.count('{') == 1 && t.count('}') == 1) )
|
|
{
|
|
trimmedLines.last() += t;
|
|
}
|
|
else
|
|
{
|
|
trimmedLines << (*it);
|
|
}
|
|
}
|
|
|
|
if (trimmedLines.first().isEmpty())
|
|
{
|
|
trimmedLines.removeFirst();
|
|
}
|
|
Template t = d->engine->newTemplate(trimmedLines.join("\n"), name);
|
|
#endif
|
|
QString output;
|
|
|
|
QTextStream textStream(&output);
|
|
NoEscapeStream stream(&textStream);
|
|
t->render(&stream, &d->context);
|
|
|
|
if (t->error() != Grantlee::NoError)
|
|
{
|
|
d->errorString = t->errorString();
|
|
}
|
|
else
|
|
{
|
|
d->errorString.clear();
|
|
}
|
|
|
|
if (d->emptyLinesPolicy == TrimEmptyLines && output.contains('\n'))
|
|
{
|
|
QStringList lines = output.split('\n', QString::KeepEmptyParts);
|
|
QMutableStringListIterator it(lines);
|
|
|
|
// Remove empty lines from the start of the document
|
|
while (it.hasNext())
|
|
{
|
|
if (it.next().trimmed().isEmpty())
|
|
{
|
|
it.remove();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Remove single empty lines
|
|
it.toFront();
|
|
bool prePreviousEmpty = false;
|
|
bool previousEmpty = false;
|
|
while (it.hasNext())
|
|
{
|
|
bool currentEmpty = it.peekNext().trimmed().isEmpty();
|
|
if (!prePreviousEmpty && previousEmpty && !currentEmpty)
|
|
{
|
|
it.remove();
|
|
}
|
|
prePreviousEmpty = previousEmpty;
|
|
previousEmpty = currentEmpty;
|
|
it.next();
|
|
}
|
|
|
|
// Compress multiple empty lines
|
|
it.toFront();
|
|
previousEmpty = false;
|
|
while (it.hasNext())
|
|
{
|
|
bool currentEmpty = it.next().trimmed().isEmpty();
|
|
if (currentEmpty && previousEmpty)
|
|
{
|
|
it.remove();
|
|
}
|
|
previousEmpty = currentEmpty;
|
|
}
|
|
|
|
// Remove empty lines from the end
|
|
it.toBack();
|
|
while (it.hasPrevious())
|
|
{
|
|
if (it.previous().trimmed().isEmpty())
|
|
{
|
|
it.remove();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add a newline to the end of file
|
|
it.toBack();
|
|
it.insert(QString());
|
|
|
|
output = lines.join("\n");
|
|
}
|
|
else if (d->emptyLinesPolicy == RemoveEmptyLines)
|
|
{
|
|
QStringList lines = output.split('\n', QString::SkipEmptyParts);
|
|
QMutableStringListIterator it(lines);
|
|
while (it.hasNext())
|
|
{
|
|
if (it.next().trimmed().isEmpty())
|
|
{
|
|
it.remove();
|
|
}
|
|
}
|
|
it.toBack();
|
|
if (lines.size() > 1)
|
|
{
|
|
it.insert(QString());
|
|
}
|
|
output = lines.join("\n");
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
QString TemplateRenderer::renderFile(const KUrl& url, const QString& name) const
|
|
{
|
|
QFile file(url.toLocalFile());
|
|
file.open(QIODevice::ReadOnly);
|
|
|
|
QString content(file.readAll());
|
|
kDebug() << content;
|
|
|
|
return render(content, name);
|
|
}
|
|
|
|
QStringList TemplateRenderer::render(const QStringList& contents) const
|
|
{
|
|
kDebug() << d->context.stackHash(0);
|
|
QStringList ret;
|
|
foreach (const QString& content, contents)
|
|
{
|
|
ret << render(content);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void TemplateRenderer::setEmptyLinesPolicy(TemplateRenderer::EmptyLinesPolicy policy)
|
|
{
|
|
d->emptyLinesPolicy = policy;
|
|
}
|
|
|
|
TemplateRenderer::EmptyLinesPolicy TemplateRenderer::emptyLinesPolicy() const
|
|
{
|
|
return d->emptyLinesPolicy;
|
|
}
|
|
|
|
DocumentChangeSet TemplateRenderer::renderFileTemplate(const SourceFileTemplate& fileTemplate,
|
|
const KUrl& baseUrl,
|
|
const QHash<QString, KUrl>& fileUrls)
|
|
{
|
|
DocumentChangeSet changes;
|
|
KUrl url(baseUrl);
|
|
|
|
url.adjustPath(KUrl::AddTrailingSlash);
|
|
QRegExp nonAlphaNumeric("\\W");
|
|
for (QHash<QString,KUrl>::const_iterator it = fileUrls.constBegin(); it != fileUrls.constEnd(); ++it)
|
|
{
|
|
QString cleanName = it.key().toLower();
|
|
cleanName.replace(nonAlphaNumeric, "_");
|
|
addVariable("output_file_" + cleanName, KUrl::relativeUrl(url, it.value()));
|
|
addVariable("output_file_" + cleanName + "_absolute", it.value().toLocalFile());
|
|
}
|
|
|
|
const KArchiveDirectory* directory = fileTemplate.directory();
|
|
ArchiveTemplateLocation location(directory);
|
|
foreach (const SourceFileTemplate::OutputFile& outputFile, fileTemplate.outputFiles())
|
|
{
|
|
const KArchiveEntry* entry = directory->entry(outputFile.fileName);
|
|
if (!entry)
|
|
{
|
|
kWarning() << "Entry" << outputFile.fileName << "is mentioned in group" << outputFile.identifier << "but is not present in the archive";
|
|
continue;
|
|
}
|
|
|
|
const KArchiveFile* file = dynamic_cast<const KArchiveFile*>(entry);
|
|
if (!file)
|
|
{
|
|
kWarning() << "Entry" << entry->name() << "is not a file";
|
|
continue;
|
|
}
|
|
|
|
KUrl url = fileUrls[outputFile.identifier];
|
|
IndexedString document(url);
|
|
SimpleRange range(SimpleCursor(0, 0), 0);
|
|
|
|
DocumentChange change(document, range, QString(), render(file->data(), outputFile.identifier));
|
|
changes.addChange(change);
|
|
kDebug() << "Added change for file" << document.str();
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
QString TemplateRenderer::errorString() const
|
|
{
|
|
return d->errorString;
|
|
}
|