Initial site generation

This commit is contained in:
James Grogan 2022-12-05 17:50:49 +00:00
parent f44c79dc1f
commit fc44290e3f
35 changed files with 667 additions and 303 deletions

View file

@ -1,8 +1,11 @@
#include "ContentFile.h"
#include "PathUtils.h"
#include "StringUtils.h"
#include "MarkdownDocument.h"
#include "MarkdownElement.h"
#include "MarkdownComponents.h"
ContentFile::ContentFile(const Path& filename)
: mFilename(filename)
@ -35,6 +38,23 @@ void ContentFile::load()
mContentBody = std::move(result.second);
}
void ContentFile::doLinkTagSubstitution(const Path& basePath)
{
auto links = mContentBody->getAllLinks();
for (const auto link : links)
{
auto target = link->getTarget();
auto replaced_target = StringUtils::removeUpTo(target, "{filename}");
if (replaced_target != target)
{
auto full_path = mFilename.parent_path() / Path(replaced_target);
auto base_relative_path = PathUtils::getRelativePath(full_path, basePath);
auto output_path = PathUtils::getPathDelimited(base_relative_path);
link->setTarget(Path(output_path).replace_extension(".html"));
}
}
}
std::string ContentFile::getMetadataItem(const std::string& key) const
{
const auto check = mMetadata.find(key);
@ -50,5 +70,6 @@ std::string ContentFile::getMetadataItem(const std::string& key) const
void ContentFile::write(const Path& path)
{
File file(path);
file.writeText(mProcessedOutput);
}

View file

@ -32,7 +32,14 @@ public:
return mContentBody.get();
}
void doLinkTagSubstitution(const Path& basePath);
void write(const Path& path);
void setProcessedOutput(const std::string& output)
{
mProcessedOutput = output;
}
protected:
Path mFilename;
FileMetadata mMetadata;

View file

@ -6,9 +6,14 @@
#include "ContentPage.h"
#include "TemplateFile.h"
#include "TemplatingEngine.h"
#include "TemplateSubstitutionContext.h"
#include "SiteGeneratorConfig.h"
#include "PathUtils.h"
#include "MarkdownConverter.h"
#include "HtmlElement.h"
#include "HtmlWriter.h"
#include "FileLogger.h"
WebsiteGenerator::WebsiteGenerator()
@ -104,18 +109,35 @@ void WebsiteGenerator::parseTemplateFiles()
{
const auto template_path = mProjectPath / mConfig->getThemePath() / mConfig->getActiveTheme();
mTemplateEngine = std::make_unique<TemplatingEngine>(template_path);
mTemplateEngine->loadTemplateFiles();
}
void WebsiteGenerator::doSubstitutions()
{
auto article_template = mTemplateEngine->processTemplate("article");
MarkdownConverter converter;
for (auto& article : mArticles)
{
article->doLinkTagSubstitution(getArticlesPath());
auto containing_div = std::make_unique<HtmlDivElement>();
converter.convert(article->getContentBody(), containing_div.get());
HtmlWriter writer;
auto md_html_content = writer.toString(containing_div.get());
TemplateSubstitutionContext sub_context;
sub_context.addSubstitution("content", md_html_content);
auto content = mTemplateEngine->renderTemplate("article", &sub_context);
article->setProcessedOutput(content);
}
}
Path WebsiteGenerator::getOutputPath() const
{
return mProjectPath / "output";
}
void WebsiteGenerator::write()
{
// Setup output dir
@ -129,6 +151,7 @@ void WebsiteGenerator::write()
auto relative_path = PathUtils::getRelativePath(article_path, getArticlesPath());
auto updated_filename = PathUtils::getPathDelimited(relative_path);
auto final_path = output_dir / Path(updated_filename).replace_extension(".html");
article->write(final_path);
}
}

View file

@ -43,6 +43,8 @@ private:
Path getConfigPath() const;
Path getOutputPath() const;
std::filesystem::path mProjectPath;
std::unique_ptr<SiteGeneratorConfig> mConfig;

View file

@ -2,22 +2,25 @@ set(MODULE_NAME compiler)
list(APPEND TARGET_HEADERS
Lexer.h
TemplatingEngine.h
TemplateFile.h
TemplateNodes.h
template_engine/TemplatingEngine.h
template_engine/TemplateFile.h
template_engine/TemplateNode.h
template_engine/TemplateElements.h
)
list(APPEND TARGET_SOURCES
Lexer.cpp
TemplatingEngine.cpp
TemplateFile.cpp
TemplateNodes.cpp
template_engine/TemplatingEngine.cpp
template_engine/TemplateFile.cpp
template_engine/TemplateNode.cpp
template_engine/TemplateElements.cpp
)
add_library(${MODULE_NAME} SHARED ${TARGET_SOURCES} ${TARGET_HEADERS})
target_include_directories(${MODULE_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/template_engine
)
target_link_libraries( ${MODULE_NAME} PUBLIC core)

View file

@ -1,124 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
class TemplateNode
{
public:
TemplateNode(TemplateNode* parent);
virtual ~TemplateNode() = default;
TemplateNode* getParent() const;
virtual void addChild(std::unique_ptr<TemplateNode> child);
std::size_t getNumChildren() const;
virtual std::string getIdentifier() const;
TemplateNode* getChild(std::size_t index) const;
virtual std::string getRawContent() const;
template<typename T>
T* getFirstChildShallow(const std::string& identifier = {}) const
{
for (const auto& child : mChildren)
{
if (auto ret = dynamic_cast<T*>(child.get()))
{
if (!identifier.empty())
{
if (child->getIdentifier() == identifier)
{
return ret;
}
}
else
{
return ret;
}
}
}
return nullptr;
}
void setExtensionParent(TemplateNode* parent);
virtual std::string render(TemplateNode* parentContext = nullptr);
protected:
std::vector<std::unique_ptr<TemplateNode> > mChildren;
TemplateNode* mParent{ nullptr };
TemplateNode* mExtensionParent{ nullptr };
};
using TemplateNodePtr = std::unique_ptr<TemplateNode>;
class TemplateExtends : public TemplateNode
{
public:
TemplateExtends(TemplateNode* parent, const std::string& path);
virtual ~TemplateExtends() = default;
std::string getRawContent() const override;
std::string getPath() const;
private:
std::string mPath;
};
class TemplateBlock : public TemplateNode
{
public:
TemplateBlock(TemplateNode* parent, const std::string& name);
virtual ~TemplateBlock() = default;
void addLine(const std::string& line);
std::string getRawContent() const override;
std::string renderAsParent(TemplateNode* base);
std::string render(TemplateNode* parentContext) override;
private:
std::string mName;
std::vector<std::string> mBody;
};
class TemplateExpression : public TemplateNode
{
public:
TemplateExpression(TemplateNode* parent, const std::string& content);
virtual ~TemplateExpression() = default;
std::string getRawContent() const override;
private:
std::string mContent;
};
class TemplateTextBody : public TemplateNode
{
public:
TemplateTextBody(TemplateNode* parent);
virtual ~TemplateTextBody() = default;
void addLine(const std::string& content);
std::string getRawContent() const override;
bool hasContent() const;
std::string render(TemplateNode* parentContext) override;
private:
std::vector<std::string> mContent;
};

View file

@ -1,67 +1,9 @@
#include "TemplateNodes.h"
#include "TemplateElements.h"
#include "StringUtils.h"
#include "TemplateSubstitutionContext.h"
TemplateNode::TemplateNode(TemplateNode* parent)
: mParent(parent)
{
}
TemplateNode* TemplateNode::getParent() const
{
return mParent;
}
void TemplateNode::addChild(std::unique_ptr<TemplateNode> child)
{
mChildren.push_back(std::move(child));
}
std::size_t TemplateNode::getNumChildren() const
{
return mChildren.size();
}
std::string TemplateNode::getIdentifier() const
{
return {};
}
TemplateNode* TemplateNode::getChild(std::size_t index) const
{
return mChildren[index].get();
}
std::string TemplateNode::getRawContent() const
{
std::string content;
for (const auto& child : mChildren)
{
content += child->getRawContent() + "\n";
}
return content;
}
void TemplateNode::setExtensionParent(TemplateNode* parent)
{
mExtensionParent = parent;
}
std::string TemplateNode::render(TemplateNode* parentContext)
{
if (!parentContext && mExtensionParent)
{
parentContext = mExtensionParent;
}
std::string content;
for (size_t idx = 0; idx < mChildren.size(); idx++)
{
content += mChildren[idx]->render(parentContext);
}
return content;
}
#include <iostream>
TemplateExtends::TemplateExtends(TemplateNode* parent, const std::string& path)
: TemplateNode(parent)
@ -98,24 +40,64 @@ std::string TemplateBlock::getRawContent() const
return content;
}
std::string TemplateBlock::renderAsParent(TemplateNode* base)
std::string TemplateBlock::getIdentifier() const
{
return {};
return mName;
}
std::string TemplateBlock::render(TemplateNode* parentContext)
std::string TemplateBlock::renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base)
{
std::string content;
for (auto& child : mChildren)
{
if (child->getType() == Type::EXPRESSION)
{
auto expression = dynamic_cast<TemplateExpression*>(child.get());
std::cout << "Got expression with content " << expression->getContent() << std::endl;
if (expression->getContent() == "super()")
{
std::cout << "Adding expression base" << std::endl;
content += base->render(substitutions, nullptr);
}
}
else if(child->getType() == Type::TEXT_BODY)
{
content += child->render(substitutions, nullptr);
}
}
return content;
}
std::string TemplateBlock::renderAsLeaf(TemplateSubstitutionContext* substitutions)
{
std::string content;
for (auto& child : mChildren)
{
if(child->getType() == Type::TEXT_BODY)
{
content += child->render(substitutions, nullptr);
}
}
return content;
}
std::string TemplateBlock::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
std::string content;
if (parentContext)
{
if (auto parent_node = parentContext->getFirstChildShallow<TemplateBlock>(getIdentifier()))
if (auto parent_node = parentContext->getFirstChildShallow(Type::BLOCK, getIdentifier()))
{
content = dynamic_cast<TemplateBlock*>(parent_node)->renderAsParent(this);
content = dynamic_cast<TemplateBlock*>(parent_node)->renderAsParent(substitutions, this);
}
else
{
content= renderAsLeaf(substitutions);
}
}
else
{
content= renderAsLeaf(substitutions);
}
return content;
}
@ -127,6 +109,20 @@ TemplateExpression::TemplateExpression(TemplateNode* parent, const std::string&
}
const std::string& TemplateExpression::getContent() const
{
return mContent;
}
std::string TemplateExpression::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
if (substitutions->hasSubstitution(mContent))
{
return substitutions->getSubstitution(mContent);
}
return {};
}
std::string TemplateExpression::getRawContent() const
{
return "TemplateExpression: " + mContent;
@ -148,7 +144,7 @@ bool TemplateTextBody::hasContent() const
return !mContent.empty();
}
std::string TemplateTextBody::render(TemplateNode* parentContext)
std::string TemplateTextBody::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
std::string content;
for (const auto& line : mContent)

View file

@ -0,0 +1,96 @@
#pragma once
#include "TemplateNode.h"
class TemplateExtends : public TemplateNode
{
public:
TemplateExtends(TemplateNode* parent, const std::string& path);
virtual ~TemplateExtends() = default;
std::string getRawContent() const override;
std::string getPath() const;
Type getType() const override
{
return Type::EXTENDS;
}
private:
std::string mPath;
};
class TemplateBlock : public TemplateNode
{
public:
TemplateBlock(TemplateNode* parent, const std::string& name);
virtual ~TemplateBlock() = default;
void addLine(const std::string& line);
std::string getRawContent() const override;
std::string getIdentifier() const override;
Type getType() const override
{
return Type::BLOCK;
}
std::string renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base);
std::string renderAsLeaf(TemplateSubstitutionContext* substitutions);
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
private:
std::string mName;
std::vector<std::string> mBody;
};
class TemplateExpression : public TemplateNode
{
public:
TemplateExpression(TemplateNode* parent, const std::string& content);
virtual ~TemplateExpression() = default;
std::string getRawContent() const override;
const std::string& getContent() const;
Type getType() const override
{
return Type::EXPRESSION;
}
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
private:
std::string mContent;
};
class TemplateTextBody : public TemplateNode
{
public:
TemplateTextBody(TemplateNode* parent);
virtual ~TemplateTextBody() = default;
void addLine(const std::string& content);
std::string getRawContent() const override;
bool hasContent() const;
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
Type getType() const override
{
return Type::TEXT_BODY;
}
private:
std::vector<std::string> mContent;
};

View file

@ -1,6 +1,7 @@
#include "TemplateFile.h"
#include "StringUtils.h"
#include "TemplateElements.h"
#include <iostream>
@ -11,9 +12,9 @@ TemplateFile::TemplateFile(const Path& path)
}
std::vector<std::string> TemplateFile::getProcessedContent() const
TemplateFile::~TemplateFile()
{
return mProcessedContent;
}
std::string TemplateFile::getName() const
@ -21,9 +22,23 @@ std::string TemplateFile::getName() const
return mPath.stem().string();
}
bool TemplateFile::hasLoaded() const
{
return mHasLoaded;
}
TemplateNode* TemplateFile::getContent() const
{
return mRootNode.get();
}
void TemplateFile::loadContent()
{
//std::cout << "Trying to load file at: " << mPath << std::endl;
if (mHasLoaded)
{
return;
}
mRawContent = File(mPath).readLines();
mWorkingLine = 0;
mWorkingNode = mRootNode.get();
@ -34,6 +49,8 @@ void TemplateFile::loadContent()
mWorkingLine++;
}
onTextBodyFinished();
mHasLoaded = true;
}
void TemplateFile::onTextBodyFinished(std::string working_string)
@ -178,12 +195,12 @@ void TemplateFile::onFoundExtends(const std::vector<std::string> args)
void TemplateFile::onFoundExpression(const std::string& expression_string)
{
auto expression = std::make_unique<TemplateExpression>(mWorkingNode, expression_string);
auto stripped = StringUtils::strip(expression_string);
auto expression = std::make_unique<TemplateExpression>(mWorkingNode, stripped);
mWorkingNode->addChild(std::move(expression));
}
void TemplateFile::dumpContent()
std::string TemplateFile::dumpContent()
{
auto content = mRootNode->getRawContent();
//std::cout << content << std::endl;
return mRootNode->getRawContent();
}

View file

@ -2,21 +2,31 @@
#include "File.h"
#include "TemplateNodes.h"
#include "TemplateNode.h"
#include <vector>
class TemplateNode;
class TemplateTextBody;
class TemplateFile
{
public:
TemplateFile(const Path& path);
std::vector<std::string> getProcessedContent() const;
~TemplateFile();
std::string dumpContent();
std::string getName() const;
TemplateNode* getContent() const;
bool hasLoaded() const;
void loadContent();
private:
void onTextBodyFinished(std::string working_string = {});
void processLine(const std::string& line);
@ -31,19 +41,12 @@ public:
void onFoundExpression(const std::string& expression_string);
void dumpContent();
TemplateNode* getContent() const
{
return mRootNode.get();
}
private:
Path mPath;
unsigned mWorkingLine{ 0 };
std::string mParentName;
std::vector<std::string> mRawContent;
std::vector<std::string> mProcessedContent;
bool mHasLoaded{false};
unsigned mWorkingLine{ 0 };
std::unique_ptr<TemplateNode> mRootNode;
TemplateNode* mWorkingNode{ nullptr };
std::unique_ptr<TemplateTextBody> mWorkingTextBody;

View file

@ -0,0 +1,91 @@
#include "TemplateNode.h"
#include "TemplateSubstitutionContext.h"
TemplateNode::TemplateNode(TemplateNode* parent)
: mParent(parent)
{
}
TemplateNode* TemplateNode::getParent() const
{
return mParent;
}
void TemplateNode::addChild(std::unique_ptr<TemplateNode> child)
{
mChildren.push_back(std::move(child));
}
std::size_t TemplateNode::getNumChildren() const
{
return mChildren.size();
}
TemplateNode* TemplateNode::getFirstChildShallow(Type type, const std::string& identifier) const
{
for (const auto& child : mChildren)
{
if (child->getType() == type)
{
if (!identifier.empty())
{
if (child->getIdentifier() == identifier)
{
return child.get();
}
}
else
{
return child.get();
}
}
}
return nullptr;
}
TemplateNode::Type TemplateNode::getType() const
{
return Type::NONE;
}
std::string TemplateNode::getIdentifier() const
{
return {};
}
TemplateNode* TemplateNode::getChild(std::size_t index) const
{
return mChildren[index].get();
}
std::string TemplateNode::getRawContent() const
{
std::string content;
for (const auto& child : mChildren)
{
content += child->getRawContent() + "\n";
}
return content;
}
void TemplateNode::setExtensionParent(TemplateNode* parent)
{
mExtensionParent = parent;
}
std::string TemplateNode::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
if (!parentContext && mExtensionParent)
{
parentContext = mExtensionParent;
}
std::string content;
for (size_t idx = 0; idx < mChildren.size(); idx++)
{
content += mChildren[idx]->render(substitutions, parentContext);
}
return content;
}

View file

@ -0,0 +1,51 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
class TemplateSubstitutionContext;
class TemplateNode
{
public:
enum class Type
{
NONE,
BLOCK,
EXPRESSION,
EXTENDS,
TEXT_BODY
};
TemplateNode(TemplateNode* parent);
virtual ~TemplateNode() = default;
virtual void addChild(std::unique_ptr<TemplateNode> child);
TemplateNode* getChild(std::size_t index) const;
TemplateNode* getFirstChildShallow(Type type, const std::string& identifier = {}) const;
virtual std::string getIdentifier() const;
std::size_t getNumChildren() const;
TemplateNode* getParent() const;
virtual std::string getRawContent() const;
virtual Type getType() const;
virtual std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext = nullptr);
void setExtensionParent(TemplateNode* parent);
protected:
std::vector<std::unique_ptr<TemplateNode> > mChildren;
TemplateNode* mParent{ nullptr };
TemplateNode* mExtensionParent{ nullptr };
};
using TemplateNodePtr = std::unique_ptr<TemplateNode>;

View file

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <unordered_map>
class TemplateSubstitutionContext
{
public:
void addSubstitution(const std::string& key, const std::string& value)
{
mSubstitutions[key] = value;
}
bool hasSubstitution(const std::string& key) const
{
return mSubstitutions.find(key) != mSubstitutions.end();
}
std::string getSubstitution(const std::string& key) const
{
auto iter = mSubstitutions.find(key);
if(iter != mSubstitutions.end())
{
return iter->second;
}
else
{
return {};
}
}
private:
std::unordered_map<std::string, std::string> mSubstitutions;
};

View file

@ -3,6 +3,9 @@
#include "Directory.h"
#include "FileLogger.h"
#include "TemplateElements.h"
#include "TemplateSubstitutionContext.h"
#include <iostream>
TemplatingEngine::TemplatingEngine(const Path& workingDirectory)
@ -11,6 +14,32 @@ TemplatingEngine::TemplatingEngine(const Path& workingDirectory)
}
std::string TemplatingEngine::renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext)
{
if (mTemplateFiles.empty())
{
loadTemplateFiles();
}
if (auto file = getTemplateFile(name))
{
if (!file->hasLoaded())
{
file->loadContent();
std::cout << file->dumpContent();
processTemplate(file, nullptr);
}
return file->getContent()->render(substitutionContext, nullptr);
}
else
{
return {};
}
}
void TemplatingEngine::loadTemplateFiles()
{
const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension);
@ -21,15 +50,6 @@ void TemplatingEngine::loadTemplateFiles()
MLOG_INFO("Found: " << mTemplateFiles.size() << " templates in " << mWorkingDirectory);
}
std::string TemplatingEngine::processTemplate(const std::string& name)
{
if (auto file = getTemplateFile(name))
{
return processTemplate(file);
}
return {};
}
TemplateFile* TemplatingEngine::getTemplateFile(const Path& path)
{
return getTemplateFile(path.stem().string());
@ -40,37 +60,24 @@ TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name)
return mTemplateFiles[name].get();
}
std::string TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent)
void TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent)
{
file->loadContent();
file->dumpContent();
std::cout << "Processing file " << file->getName() << std::endl;
auto content = file->getContent();
if (parent)
{
std::cout << "Setting extension parent" << std::endl;
content->setExtensionParent(parent);
}
if (auto extension_node = content->getFirstChildShallow<TemplateExtends>())
if (auto extension_node = dynamic_cast<TemplateExtends*>(content->getFirstChildShallow(TemplateNode::Type::EXTENDS)))
{
//std::cout << "Found extension node" << std::endl;
std::cout << "Found extension node " << std::endl;
if (auto extension_template = getTemplateFile(Path(extension_node->getPath())))
{
//std::cout << "Found extension template" << std::endl;
return processTemplate(extension_template, parent);
}
else
{
return render(content);
std::cout << "Found extension template " << std::endl;
processTemplate(extension_template, content);
}
}
else
{
return render(content);
}
}
std::string TemplatingEngine::render(TemplateNode* content)
{
return content->render();
}

View file

@ -8,20 +8,21 @@
#include <memory>
#include <unordered_map>
class TemplateSubstitutionContext;
class TemplatingEngine
{
public:
TemplatingEngine(const Path& workingDirectory);
void loadTemplateFiles();
std::string renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext);
std::string processTemplate(const std::string& name);
private:
TemplateFile* getTemplateFile(const std::string& name);
TemplateFile* getTemplateFile(const Path& path);
std::string render(TemplateNode* content);
std::string processTemplate(TemplateFile* file, TemplateNode* parent = nullptr);
void loadTemplateFiles();
void processTemplate(TemplateFile* file, TemplateNode* parent = nullptr);
std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
Path mWorkingDirectory;

View file

@ -53,13 +53,11 @@ std::string StringUtils::strip(const std::string& input)
return {};
}
std::locale loc;
std::string working_string;
std::size_t first_nonspace = 0;
std::size_t last_nonspace = working_string.size() - 1;
for (std::size_t idx = 0; idx < working_string.size(); idx++)
std::size_t last_nonspace = input.size() - 1;
for (std::size_t idx = 0; idx < input.size(); idx++)
{
if (!std::isspace(working_string[idx], loc))
if (!std::isspace(input[idx]))
{
first_nonspace = idx;
break;
@ -73,13 +71,13 @@ std::string StringUtils::strip(const std::string& input)
for (std::size_t idx = last_nonspace; idx > 0; idx--)
{
if (!std::isspace(working_string[idx], loc))
if (!std::isspace(input[idx]))
{
last_nonspace = idx;
break;
}
}
return working_string.substr(first_nonspace, last_nonspace-first_nonspace);
return input.substr(first_nonspace, last_nonspace-first_nonspace + 1);
}
std::vector<std::string> StringUtils::split(const std::string& input)
@ -166,3 +164,21 @@ std::string StringUtils::stripQuotes(const std::string& input)
}
return input.substr(start_index, end_index - start_index + 1);
}
std::string StringUtils::replaceWith(const std::string& inputString, const std::string& searchString, const std::string& replaceString)
{
return inputString;
}
std::string StringUtils::removeUpTo(const std::string& input, const std::string& prefix)
{
std::size_t found = input.find(prefix);
if (found!=std::string::npos)
{
return input.substr(found, prefix.size());
}
else
{
return input;
}
}

View file

@ -27,6 +27,8 @@ public:
static std::vector<std::string> split(const std::string& input);
static std::string strip(const std::string& input);
static std::string removeUpTo(const std::string& input, const std::string& prefix);
static std::vector<std::string> toLines(const std::string& input);
static std::string stripQuotes(const std::string& input);
@ -34,4 +36,6 @@ public:
static std::vector<unsigned char> toBytes(const std::string& input);
static std::string toString(const std::vector<unsigned char>& bytes);
static std::string replaceWith(const std::string& inputString, const std::string& searchString, const std::string& replaceString);
};

View file

@ -3,6 +3,7 @@
#include "MarkdownParser.h"
#include "MarkdownDocument.h"
#include "MarkdownConverter.h"
#include "MarkdownElement.h"
#include "HtmlDocument.h"
#include "HtmlWriter.h"

View file

@ -31,3 +31,8 @@ void HtmlDocument::addElementToBody(std::unique_ptr<HtmlElement> element)
body_element->addChild(std::move(element));
}
}
HtmlBodyElement* HtmlDocument::getBodyElement() const
{
return dynamic_cast<HtmlBodyElement*>(getRoot()->getFirstChildWithTagName("body"));
}

View file

@ -5,6 +5,7 @@
#include <memory>
class HtmlElement;
class HtmlBodyElement;
class HtmlDocument : public XmlDocument
{
@ -17,6 +18,8 @@ public:
void addElementToBody(std::unique_ptr<HtmlElement> element);
HtmlBodyElement* getBodyElement() const;
private:
};

View file

@ -19,7 +19,8 @@ public:
HYPERLINK,
IMAGE,
UNORDERED_LIST,
LIST_ITEM
LIST_ITEM,
DIV
};
HtmlElement(const std::string& tagName);
@ -27,6 +28,20 @@ public:
virtual Type getType() const = 0;
};
class HtmlDivElement : public HtmlElement
{
public:
HtmlDivElement() : HtmlElement("div")
{
}
Type getType() const override
{
return Type::DIV;
}
};
class HtmlCodeElement : public HtmlElement
{
public:

View file

@ -1,16 +1,20 @@
#include "HtmlWriter.h"
#include "HtmlDocument.h"
#include "HtmlElement.h"
#include "XmlElement.h"
#include "XmlAttribute.h"
#include <iostream>
HtmlWriter::HtmlWriter()
{
}
std::string HtmlWriter::toString(HtmlElement* element, unsigned startDepth)
{
return element->toString(startDepth);
}
std::string HtmlWriter::toString(HtmlDocument* document)
{
std::string content = "<!DOCTYPE html>\n";

View file

@ -3,6 +3,7 @@
#include <string>
class HtmlDocument;
class HtmlElement;
class HtmlWriter
{
@ -10,4 +11,6 @@ public:
HtmlWriter();
std::string toString(HtmlDocument* document);
std::string toString(HtmlElement* element, unsigned startDepth=0);
};

View file

@ -1,5 +1,7 @@
#include "MarkdownComponents.h"
#include "StringUtils.h"
MarkdownTextSpan::Type MarkdownTextSpan::getType() const
{
return Type::TEXT_SPAN;
@ -87,6 +89,13 @@ MarkdownLink::Type MarkdownLink::getType() const
return Type::LINK;
}
void MarkdownLink::doFieldSubstitution(Type elementType, const std::string& searchPhrase, const std::string& replacementPhrase)
{
if (elementType == Type::LINK)
{
mTarget = StringUtils::replaceWith(mTarget, searchPhrase, replacementPhrase);
}
}
MarkdownImage::MarkdownImage(const std::string& source, const std::string& alt)
: mSource(source),

View file

@ -13,6 +13,28 @@ public:
Type getType() const override;
};
class MarkdownLink : public MarkdownInlineElement
{
public:
MarkdownLink(const std::string& target);
virtual ~MarkdownLink() = default;
const std::string& getTarget() const;
Type getType() const override;
void setTarget(const std::string& target)
{
mTarget = target;
}
void doFieldSubstitution(Type elementType, const std::string& searchPhrase, const std::string& replacementPhrase) override;
private:
std::string mTarget;
};
class MarkdownParagraph : public MarkdownElement
{
public:
@ -26,6 +48,27 @@ public:
MarkdownInlineElement* getChild(std::size_t idx) const;
void doFieldSubstitution(Type elementType, const std::string& searchPhrase, const std::string& replacementPhrase) override
{
for(auto& child : mChildren)
{
child->doFieldSubstitution(elementType, searchPhrase, replacementPhrase);
}
}
std::vector<MarkdownLink*> getAllLinks() const
{
std::vector<MarkdownLink*> links;
for(auto& child : mChildren)
{
if (child->getType() == Type::LINK)
{
links.push_back(dynamic_cast<MarkdownLink*>(child.get()));
}
}
return links;
}
private:
std::vector<std::unique_ptr<MarkdownInlineElement> > mChildren;
};
@ -79,20 +122,6 @@ public:
Type getType() const override;
};
class MarkdownLink : public MarkdownInlineElement
{
public:
MarkdownLink(const std::string& target);
virtual ~MarkdownLink() = default;
const std::string& getTarget() const;
Type getType() const override;
private:
std::string mTarget;
};
class MarkdownImage : public MarkdownInlineElement
{
public:

View file

@ -3,16 +3,15 @@
#include "HtmlDocument.h"
#include "HtmlElement.h"
#include "HtmlParagraphElement.h"
#include "HtmlBodyElement.h"
#include "HtmlTextRun.h"
#include "MarkdownElement.h"
#include "MarkdownComponents.h"
#include "MarkdownDocument.h"
std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markdownDoc) const
void MarkdownConverter::convert(MarkdownDocument* markdownDoc, HtmlElement* parentElement) const
{
auto html_doc = std::make_unique<HtmlDocument>();
for(unsigned idx=0; idx<markdownDoc->getNumElements();idx++)
{
auto md_element = markdownDoc->getElement(idx);
@ -23,7 +22,7 @@ std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markd
auto html_element = std::make_unique<HtmlHeadingElement>(heading_level);
html_element->setText(md_element->getTextContent());
html_doc->addElementToBody(std::move(html_element));
parentElement->addChild(std::move(html_element));
}
else if(md_element->getType() == MarkdownElement::Type::PARAGRAPH)
{
@ -59,7 +58,7 @@ std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markd
html_p_element->addChild(std::move(html_text));
}
}
html_doc->addElementToBody(std::move(html_p_element));
parentElement->addChild(std::move(html_p_element));
}
else if(md_element->getType() == MarkdownElement::Type::BULLET_LIST)
{
@ -72,15 +71,23 @@ std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markd
html_list_item->setText(child->getTextContent());
html_list->addChild(std::move(html_list_item));
}
html_doc->addElementToBody(std::move(html_list));
parentElement->addChild(std::move(html_list));
}
else if(md_element->getType() == MarkdownElement::Type::MULTILINE_QUOTE)
{
auto html_quote = std::make_unique<HtmlCodeElement>();
html_quote->setText(md_element->getTextContent());
html_doc->addElementToBody(std::move(html_quote));
parentElement->addChild(std::move(html_quote));
}
}
}
std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markdownDoc) const
{
auto html_doc = std::make_unique<HtmlDocument>();
auto body_element = html_doc->getBodyElement();
convert(markdownDoc, body_element);
return std::move(html_doc);
}

View file

@ -3,6 +3,7 @@
#include <memory>
class HtmlDocument;
class HtmlElement;
class MarkdownDocument;
class MarkdownConverter
@ -10,4 +11,6 @@ class MarkdownConverter
public:
std::unique_ptr<HtmlDocument> convert(MarkdownDocument* markdownDoc) const;
void convert(MarkdownDocument* markdownDoc, HtmlElement* parentElement) const;
};

View file

@ -1,6 +1,8 @@
#include "MarkdownDocument.h"
#include "MarkdownElement.h"
#include "MarkdownComponents.h"
void MarkdownDocument::addElement(std::unique_ptr<MarkdownElement> element)
{
@ -16,3 +18,25 @@ MarkdownElement* MarkdownDocument::getElement(std::size_t idx) const
{
return mElements[idx].get();
}
void MarkdownDocument::doLinkTargetSubstitution(const std::string& targetString, const std::string& replacementString)
{
for(auto& element : mElements)
{
element->doFieldSubstitution(MarkdownElement::Type::LINK, targetString, replacementString);
}
}
std::vector<MarkdownLink*> MarkdownDocument::getAllLinks() const
{
std::vector<MarkdownLink*> links;
for(auto& element : mElements)
{
if (element->getType() == MarkdownElement::Type::PARAGRAPH)
{
auto para_links = dynamic_cast<MarkdownParagraph*>(element.get())->getAllLinks();
links.insert(links.end(), para_links.begin(), para_links.end());
}
}
return links;
}

View file

@ -4,6 +4,7 @@
#include <memory>
class MarkdownElement;
class MarkdownLink;
class MarkdownDocument
{
@ -14,6 +15,10 @@ public:
MarkdownElement* getElement(std::size_t idx) const;
void doLinkTargetSubstitution(const std::string& targetString, const std::string& replacementString);
std::vector<MarkdownLink*> getAllLinks() const;
private:
std::vector<std::unique_ptr<MarkdownElement> > mElements;
};

View file

@ -29,6 +29,11 @@ public:
const std::string& getTextContent() const;
virtual Type getType() const = 0;
virtual void doFieldSubstitution(Type elementType, const std::string& searchPhrase, const std::string& replacementPhrase)
{
}
private:
std::string mTextContent;
};

View file

@ -19,7 +19,6 @@ MarkdownParser::~MarkdownParser()
void MarkdownParser::onMultilineQuote()
{
std::cout << "Adding multiline quote " << mDocumentContent << std::endl;
auto quote = std::make_unique<MarkdownMultilineQuote>(mWorkingTag);
quote->appendTextContent(mDocumentContent);
@ -34,8 +33,6 @@ void MarkdownParser::onMultilineQuote()
void MarkdownParser::onInlineQuote()
{
std::cout << "Adding inline quote " << mLineContent << std::endl;
auto quote = std::make_unique<MarkdownInlineQuote>();
quote->appendTextContent(mLineContent);
mLineContent.clear();
@ -49,8 +46,6 @@ void MarkdownParser::onInlineQuote()
void MarkdownParser::onHeading(unsigned level)
{
std::cout << "Adding heading: " << mLineContent << std::endl;
auto heading = std::make_unique<MarkdownHeading>(level);
heading->appendTextContent(mLineContent);
mMarkdownDocument->addElement(std::move(heading));
@ -60,7 +55,6 @@ void MarkdownParser::onNewParagraph()
{
if (mWorkingBulletList)
{
std::cout << "Adding bullets to document" << std::endl;
mMarkdownDocument->addElement(std::move(mWorkingBulletList));
mWorkingBulletList.reset();
mDocumentState == DocumentState::NONE;
@ -71,7 +65,6 @@ void MarkdownParser::onNewParagraph()
if (!mWorkingParagraph->getNumChildren() == 0)
{
std::cout << "Adding para to document" << std::endl;
mMarkdownDocument->addElement(std::move(mWorkingParagraph));
}
}
@ -84,8 +77,6 @@ void MarkdownParser::onTextSpan()
if(mWorkingParagraph && !mDocumentContent.empty())
{
std::cout << "Adding text " << mDocumentContent << std::endl;
auto text_span = std::make_unique<MarkdownTextSpan>();
text_span->appendTextContent(mDocumentContent);
mWorkingParagraph->addChild(std::move(text_span));
@ -141,7 +132,6 @@ std::pair<unsigned, bool> MarkdownParser::onTick(unsigned tickCount)
void MarkdownParser::onLink()
{
std::cout << "Adding hyperlink to " << mLineContent << " with tag " << mWorkingTag << std::endl;
auto element = std::make_unique<MarkdownLink>(mLineContent);
mLineContent.clear();
@ -157,7 +147,6 @@ void MarkdownParser::onLink()
void MarkdownParser::onImage()
{
std::cout << "Adding image with path " << mLineContent << " and alt" << mWorkingTag << std::endl;
auto element = std::make_unique<MarkdownImage>(mLineContent, mWorkingTag);
mLineContent.clear();
@ -173,8 +162,6 @@ void MarkdownParser::onImage()
void MarkdownParser::onBulletItem()
{
std::cout << "Adding bullet item " << mLineContent << std::endl;
if (!mWorkingBulletList)
{
mWorkingBulletList = std::make_unique<MarkdownBulletList>();
@ -231,7 +218,6 @@ void MarkdownParser::processLine()
{
if (!flushed_pre_inline)
{
std::cout << "Flushing pre-line " << std::endl;
mDocumentContent += mLineContent;
onTextSpan();
flushed_pre_inline = true;

View file

@ -8,8 +8,7 @@
TEST_CASE(TestTemplatingEngine, "compiler")
{
auto engine = TemplatingEngine(TestUtils::getTestDataDir());
engine.loadTemplateFiles();
const auto content = engine.processTemplate("index");
const auto content = engine.renderTemplate("index", nullptr);
File outfile(TestUtils::getTestOutputDir() / "index.html");
outfile.writeText(content);

View file

@ -1,8 +1,9 @@
set(CORE_UNIT_TEST_FILES
core/TestByteUtils.cpp
core/TestBitStream.cpp
core/TestTomlReader.cpp
core/TestDataStructures.cpp
core/TestTomlReader.cpp
core/TestStringUtils.cpp
PARENT_SCOPE
)

View file

@ -0,0 +1,15 @@
#include "StringUtils.h"
#include "TestFramework.h"
#include "TestUtils.h"
#include <iostream>
TEST_CASE(TestStringUtils_Strip, "core")
{
std::string input = " super() ";
std::string stripped = StringUtils::strip(input);
auto predicate = stripped == "super()";
REQUIRE(predicate);
}

View file

@ -5,6 +5,7 @@
#include "HtmlDocument.h"
#include "MarkdownDocument.h"
#include "MarkdownConverter.h"
#include "MarkdownElement.h"
#include "HtmlWriter.h"
#include "TestFramework.h"