diff --git a/apps/website-generator/ContentFile.cpp b/apps/website-generator/ContentFile.cpp index a7ca1f4..aedd0b1 100644 --- a/apps/website-generator/ContentFile.cpp +++ b/apps/website-generator/ContentFile.cpp @@ -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); } diff --git a/apps/website-generator/ContentFile.h b/apps/website-generator/ContentFile.h index 3382f2c..993a0a8 100644 --- a/apps/website-generator/ContentFile.h +++ b/apps/website-generator/ContentFile.h @@ -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; diff --git a/apps/website-generator/WebsiteGenerator.cpp b/apps/website-generator/WebsiteGenerator.cpp index 6db378f..82cd8b0 100644 --- a/apps/website-generator/WebsiteGenerator.cpp +++ b/apps/website-generator/WebsiteGenerator.cpp @@ -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(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(); + 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); } } diff --git a/apps/website-generator/WebsiteGenerator.h b/apps/website-generator/WebsiteGenerator.h index 343ad81..1ca0527 100644 --- a/apps/website-generator/WebsiteGenerator.h +++ b/apps/website-generator/WebsiteGenerator.h @@ -43,6 +43,8 @@ private: Path getConfigPath() const; + Path getOutputPath() const; + std::filesystem::path mProjectPath; std::unique_ptr mConfig; diff --git a/src/compiler/CMakeLists.txt b/src/compiler/CMakeLists.txt index 7f6390f..291c7c4 100644 --- a/src/compiler/CMakeLists.txt +++ b/src/compiler/CMakeLists.txt @@ -2,24 +2,27 @@ 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) set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) -set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src) \ No newline at end of file +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src) diff --git a/src/compiler/TemplateNodes.h b/src/compiler/TemplateNodes.h deleted file mode 100644 index dae7e80..0000000 --- a/src/compiler/TemplateNodes.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include -#include -#include - -class TemplateNode -{ -public: - TemplateNode(TemplateNode* parent); - - virtual ~TemplateNode() = default; - - TemplateNode* getParent() const; - - virtual void addChild(std::unique_ptr child); - - std::size_t getNumChildren() const; - - virtual std::string getIdentifier() const; - - TemplateNode* getChild(std::size_t index) const; - - virtual std::string getRawContent() const; - - template - T* getFirstChildShallow(const std::string& identifier = {}) const - { - for (const auto& child : mChildren) - { - if (auto ret = dynamic_cast(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 > mChildren; - TemplateNode* mParent{ nullptr }; - TemplateNode* mExtensionParent{ nullptr }; -}; - -using TemplateNodePtr = std::unique_ptr; - -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 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 mContent; -}; diff --git a/src/compiler/TemplateNodes.cpp b/src/compiler/template_engine/TemplateElements.cpp similarity index 51% rename from src/compiler/TemplateNodes.cpp rename to src/compiler/template_engine/TemplateElements.cpp index 78881f3..4a18fed 100644 --- a/src/compiler/TemplateNodes.cpp +++ b/src/compiler/template_engine/TemplateElements.cpp @@ -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 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 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(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(getIdentifier())) + if (auto parent_node = parentContext->getFirstChildShallow(Type::BLOCK, getIdentifier())) { - content = dynamic_cast(parent_node)->renderAsParent(this); + content = dynamic_cast(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) diff --git a/src/compiler/template_engine/TemplateElements.h b/src/compiler/template_engine/TemplateElements.h new file mode 100644 index 0000000..54b8ebd --- /dev/null +++ b/src/compiler/template_engine/TemplateElements.h @@ -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 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 mContent; +}; diff --git a/src/compiler/TemplateFile.cpp b/src/compiler/template_engine/TemplateFile.cpp similarity index 90% rename from src/compiler/TemplateFile.cpp rename to src/compiler/template_engine/TemplateFile.cpp index 3a8f3ed..5368ee6 100644 --- a/src/compiler/TemplateFile.cpp +++ b/src/compiler/template_engine/TemplateFile.cpp @@ -1,19 +1,20 @@ #include "TemplateFile.h" #include "StringUtils.h" +#include "TemplateElements.h" #include TemplateFile::TemplateFile(const Path& path) : mPath(path), - mRootNode(std::make_unique(nullptr)) + mRootNode(std::make_unique(nullptr)) { } -std::vector 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 args) void TemplateFile::onFoundExpression(const std::string& expression_string) { - auto expression = std::make_unique(mWorkingNode, expression_string); + auto stripped = StringUtils::strip(expression_string); + auto expression = std::make_unique(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(); } diff --git a/src/compiler/TemplateFile.h b/src/compiler/template_engine/TemplateFile.h similarity index 78% rename from src/compiler/TemplateFile.h rename to src/compiler/template_engine/TemplateFile.h index c80e656..c4a5b13 100644 --- a/src/compiler/TemplateFile.h +++ b/src/compiler/template_engine/TemplateFile.h @@ -2,21 +2,31 @@ #include "File.h" -#include "TemplateNodes.h" +#include "TemplateNode.h" #include +class TemplateNode; +class TemplateTextBody; + class TemplateFile { public: TemplateFile(const Path& path); - std::vector 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 mRawContent; - std::vector mProcessedContent; + bool mHasLoaded{false}; + + unsigned mWorkingLine{ 0 }; std::unique_ptr mRootNode; TemplateNode* mWorkingNode{ nullptr }; std::unique_ptr mWorkingTextBody; diff --git a/src/compiler/template_engine/TemplateNode.cpp b/src/compiler/template_engine/TemplateNode.cpp new file mode 100644 index 0000000..732157e --- /dev/null +++ b/src/compiler/template_engine/TemplateNode.cpp @@ -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 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; +} diff --git a/src/compiler/template_engine/TemplateNode.h b/src/compiler/template_engine/TemplateNode.h new file mode 100644 index 0000000..06cc64a --- /dev/null +++ b/src/compiler/template_engine/TemplateNode.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +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 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 > mChildren; + TemplateNode* mParent{ nullptr }; + TemplateNode* mExtensionParent{ nullptr }; +}; + +using TemplateNodePtr = std::unique_ptr; diff --git a/src/compiler/template_engine/TemplateSubstitutionContext.h b/src/compiler/template_engine/TemplateSubstitutionContext.h new file mode 100644 index 0000000..c125e1a --- /dev/null +++ b/src/compiler/template_engine/TemplateSubstitutionContext.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +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 mSubstitutions; +}; diff --git a/src/compiler/TemplatingEngine.cpp b/src/compiler/template_engine/TemplatingEngine.cpp similarity index 52% rename from src/compiler/TemplatingEngine.cpp rename to src/compiler/template_engine/TemplatingEngine.cpp index 9cb7999..008807a 100644 --- a/src/compiler/TemplatingEngine.cpp +++ b/src/compiler/template_engine/TemplatingEngine.cpp @@ -3,6 +3,9 @@ #include "Directory.h" #include "FileLogger.h" +#include "TemplateElements.h" +#include "TemplateSubstitutionContext.h" + #include 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()) + if (auto extension_node = dynamic_cast(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(); } diff --git a/src/compiler/TemplatingEngine.h b/src/compiler/template_engine/TemplatingEngine.h similarity index 71% rename from src/compiler/TemplatingEngine.h rename to src/compiler/template_engine/TemplatingEngine.h index 9c92deb..e622847 100644 --- a/src/compiler/TemplatingEngine.h +++ b/src/compiler/template_engine/TemplatingEngine.h @@ -8,20 +8,21 @@ #include #include +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 > mTemplateFiles; Path mWorkingDirectory; diff --git a/src/core/StringUtils.cpp b/src/core/StringUtils.cpp index 467b1e2..b1fa58c 100644 --- a/src/core/StringUtils.cpp +++ b/src/core/StringUtils.cpp @@ -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 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; + } +} diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index 41dbf42..ab1603d 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -27,6 +27,8 @@ public: static std::vector 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 toLines(const std::string& input); static std::string stripQuotes(const std::string& input); @@ -34,4 +36,6 @@ public: static std::vector toBytes(const std::string& input); static std::string toString(const std::vector& bytes); + static std::string replaceWith(const std::string& inputString, const std::string& searchString, const std::string& replaceString); + }; diff --git a/src/publishing/DocumentConverter.cpp b/src/publishing/DocumentConverter.cpp index 9544f00..e9777c8 100644 --- a/src/publishing/DocumentConverter.cpp +++ b/src/publishing/DocumentConverter.cpp @@ -3,6 +3,7 @@ #include "MarkdownParser.h" #include "MarkdownDocument.h" #include "MarkdownConverter.h" +#include "MarkdownElement.h" #include "HtmlDocument.h" #include "HtmlWriter.h" diff --git a/src/web/html/HtmlDocument.cpp b/src/web/html/HtmlDocument.cpp index 7f437ce..983abbd 100644 --- a/src/web/html/HtmlDocument.cpp +++ b/src/web/html/HtmlDocument.cpp @@ -31,3 +31,8 @@ void HtmlDocument::addElementToBody(std::unique_ptr element) body_element->addChild(std::move(element)); } } + +HtmlBodyElement* HtmlDocument::getBodyElement() const +{ + return dynamic_cast(getRoot()->getFirstChildWithTagName("body")); +} diff --git a/src/web/html/HtmlDocument.h b/src/web/html/HtmlDocument.h index b42a335..01ffd99 100644 --- a/src/web/html/HtmlDocument.h +++ b/src/web/html/HtmlDocument.h @@ -5,6 +5,7 @@ #include class HtmlElement; +class HtmlBodyElement; class HtmlDocument : public XmlDocument { @@ -17,6 +18,8 @@ public: void addElementToBody(std::unique_ptr element); + HtmlBodyElement* getBodyElement() const; + private: }; diff --git a/src/web/html/HtmlElement.h b/src/web/html/HtmlElement.h index 472fcaa..4a52e91 100644 --- a/src/web/html/HtmlElement.h +++ b/src/web/html/HtmlElement.h @@ -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: diff --git a/src/web/html/HtmlWriter.cpp b/src/web/html/HtmlWriter.cpp index f745ff3..5efd92e 100644 --- a/src/web/html/HtmlWriter.cpp +++ b/src/web/html/HtmlWriter.cpp @@ -1,16 +1,20 @@ #include "HtmlWriter.h" #include "HtmlDocument.h" +#include "HtmlElement.h" #include "XmlElement.h" #include "XmlAttribute.h" -#include - HtmlWriter::HtmlWriter() { } +std::string HtmlWriter::toString(HtmlElement* element, unsigned startDepth) +{ + return element->toString(startDepth); +} + std::string HtmlWriter::toString(HtmlDocument* document) { std::string content = "\n"; diff --git a/src/web/html/HtmlWriter.h b/src/web/html/HtmlWriter.h index acca71c..cb33859 100644 --- a/src/web/html/HtmlWriter.h +++ b/src/web/html/HtmlWriter.h @@ -3,6 +3,7 @@ #include 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); }; diff --git a/src/web/markdown/MarkdownComponents.cpp b/src/web/markdown/MarkdownComponents.cpp index ca7b739..45c8792 100644 --- a/src/web/markdown/MarkdownComponents.cpp +++ b/src/web/markdown/MarkdownComponents.cpp @@ -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), diff --git a/src/web/markdown/MarkdownComponents.h b/src/web/markdown/MarkdownComponents.h index 6b19ddf..bea3d0d 100644 --- a/src/web/markdown/MarkdownComponents.h +++ b/src/web/markdown/MarkdownComponents.h @@ -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 getAllLinks() const + { + std::vector links; + for(auto& child : mChildren) + { + if (child->getType() == Type::LINK) + { + links.push_back(dynamic_cast(child.get())); + } + } + return links; + } + private: std::vector > 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: diff --git a/src/web/markdown/MarkdownConverter.cpp b/src/web/markdown/MarkdownConverter.cpp index 2a83dc1..7dbeb2e 100644 --- a/src/web/markdown/MarkdownConverter.cpp +++ b/src/web/markdown/MarkdownConverter.cpp @@ -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 MarkdownConverter::convert(MarkdownDocument* markdownDoc) const +void MarkdownConverter::convert(MarkdownDocument* markdownDoc, HtmlElement* parentElement) const { - auto html_doc = std::make_unique(); - for(unsigned idx=0; idxgetNumElements();idx++) { auto md_element = markdownDoc->getElement(idx); @@ -23,7 +22,7 @@ std::unique_ptr MarkdownConverter::convert(MarkdownDocument* markd auto html_element = std::make_unique(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 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 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(); html_quote->setText(md_element->getTextContent()); - html_doc->addElementToBody(std::move(html_quote)); + parentElement->addChild(std::move(html_quote)); } } +} + +std::unique_ptr MarkdownConverter::convert(MarkdownDocument* markdownDoc) const +{ + auto html_doc = std::make_unique(); + + auto body_element = html_doc->getBodyElement(); + convert(markdownDoc, body_element); return std::move(html_doc); } diff --git a/src/web/markdown/MarkdownConverter.h b/src/web/markdown/MarkdownConverter.h index aa37a97..0626027 100644 --- a/src/web/markdown/MarkdownConverter.h +++ b/src/web/markdown/MarkdownConverter.h @@ -3,6 +3,7 @@ #include class HtmlDocument; +class HtmlElement; class MarkdownDocument; class MarkdownConverter @@ -10,4 +11,6 @@ class MarkdownConverter public: std::unique_ptr convert(MarkdownDocument* markdownDoc) const; + void convert(MarkdownDocument* markdownDoc, HtmlElement* parentElement) const; + }; diff --git a/src/web/markdown/MarkdownDocument.cpp b/src/web/markdown/MarkdownDocument.cpp index 2db073a..4007aaf 100644 --- a/src/web/markdown/MarkdownDocument.cpp +++ b/src/web/markdown/MarkdownDocument.cpp @@ -1,6 +1,8 @@ #include "MarkdownDocument.h" #include "MarkdownElement.h" +#include "MarkdownComponents.h" + void MarkdownDocument::addElement(std::unique_ptr 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 MarkdownDocument::getAllLinks() const +{ + std::vector links; + for(auto& element : mElements) + { + if (element->getType() == MarkdownElement::Type::PARAGRAPH) + { + auto para_links = dynamic_cast(element.get())->getAllLinks(); + links.insert(links.end(), para_links.begin(), para_links.end()); + } + } + return links; +} diff --git a/src/web/markdown/MarkdownDocument.h b/src/web/markdown/MarkdownDocument.h index 6a02655..7e3ba8c 100644 --- a/src/web/markdown/MarkdownDocument.h +++ b/src/web/markdown/MarkdownDocument.h @@ -4,6 +4,7 @@ #include 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 getAllLinks() const; + private: std::vector > mElements; }; diff --git a/src/web/markdown/MarkdownElement.h b/src/web/markdown/MarkdownElement.h index cb752d1..7af2780 100644 --- a/src/web/markdown/MarkdownElement.h +++ b/src/web/markdown/MarkdownElement.h @@ -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; }; diff --git a/src/web/markdown/MarkdownParser.cpp b/src/web/markdown/MarkdownParser.cpp index 72912e9..f76306f 100644 --- a/src/web/markdown/MarkdownParser.cpp +++ b/src/web/markdown/MarkdownParser.cpp @@ -19,7 +19,6 @@ MarkdownParser::~MarkdownParser() void MarkdownParser::onMultilineQuote() { - std::cout << "Adding multiline quote " << mDocumentContent << std::endl; auto quote = std::make_unique(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(); 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(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(); text_span->appendTextContent(mDocumentContent); mWorkingParagraph->addChild(std::move(text_span)); @@ -141,7 +132,6 @@ std::pair MarkdownParser::onTick(unsigned tickCount) void MarkdownParser::onLink() { - std::cout << "Adding hyperlink to " << mLineContent << " with tag " << mWorkingTag << std::endl; auto element = std::make_unique(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(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(); @@ -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; diff --git a/test/compiler/TestTemplatingEngine.cpp b/test/compiler/TestTemplatingEngine.cpp index 3649455..852b916 100644 --- a/test/compiler/TestTemplatingEngine.cpp +++ b/test/compiler/TestTemplatingEngine.cpp @@ -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); diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 3e7ddb9..f4b5cdf 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -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 ) diff --git a/test/core/TestStringUtils.cpp b/test/core/TestStringUtils.cpp new file mode 100644 index 0000000..5edb5c0 --- /dev/null +++ b/test/core/TestStringUtils.cpp @@ -0,0 +1,15 @@ +#include "StringUtils.h" + +#include "TestFramework.h" +#include "TestUtils.h" + +#include + +TEST_CASE(TestStringUtils_Strip, "core") +{ + std::string input = " super() "; + std::string stripped = StringUtils::strip(input); + + auto predicate = stripped == "super()"; + REQUIRE(predicate); +} diff --git a/test/web/TestMarkdownParser.cpp b/test/web/TestMarkdownParser.cpp index 5b0eefa..00fdadf 100644 --- a/test/web/TestMarkdownParser.cpp +++ b/test/web/TestMarkdownParser.cpp @@ -5,6 +5,7 @@ #include "HtmlDocument.h" #include "MarkdownDocument.h" #include "MarkdownConverter.h" +#include "MarkdownElement.h" #include "HtmlWriter.h" #include "TestFramework.h"