Allow custom tag processing in md

This commit is contained in:
James Grogan 2022-12-07 11:34:29 +00:00
parent 22157169c0
commit 101bfb4207
12 changed files with 214 additions and 43 deletions

View file

@ -3,6 +3,7 @@
#include "MarkdownParser.h" #include "MarkdownParser.h"
#include "MarkdownDocument.h" #include "MarkdownDocument.h"
#include "MarkdownElement.h" #include "MarkdownElement.h"
#include "MarkdownCustomElement.h"
#include "File.h" #include "File.h"
@ -37,9 +38,20 @@ std::pair<MarkdownContentParser::FileMetadata, std::unique_ptr<MarkdownDocument>
} }
MarkdownParser md_parser; MarkdownParser md_parser;
auto content = md_parser.run(content_body);
return {output_metadata, std::move(content)}; auto document = std::make_unique<MarkdownDocument>();
auto custom_math_inline = std::make_unique<MarkdownCustomInlineElementContext>("$");
custom_math_inline->setReplacementDelimiters("\\(", "\\)");
auto custom_math_multiline = std::make_unique<MarkdownCustomMultilineElementContext>("$$");
document->registerCustomInlineElement(std::move(custom_math_inline));
document->registerCustomMultilineElement(std::move(custom_math_multiline));
md_parser.run(content_body, document.get());
return {output_metadata, std::move(document)};
} }
std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::checkForMetadataItem(const std::string& line) const std::optional<MarkdownContentParser::FileMetadataItem> MarkdownContentParser::checkForMetadataItem(const std::string& line) const

View file

@ -1,5 +1,6 @@
#include "MarkdownComponents.h" #include "MarkdownComponents.h"
#include "MarkdownCustomElement.h"
#include "StringUtils.h" #include "StringUtils.h"
MarkdownTextSpan::Type MarkdownTextSpan::getType() const MarkdownTextSpan::Type MarkdownTextSpan::getType() const
@ -98,8 +99,8 @@ MarkdownInlineQuote::Type MarkdownInlineQuote::getType() const
return Type::INLINE_QUOTE; return Type::INLINE_QUOTE;
} }
MarkdownCustomInline::MarkdownCustomInline(const std::string& delimiter) MarkdownCustomInline::MarkdownCustomInline(MarkdownCustomElementContext* customElement)
: mDelimiter(delimiter) : mCustomElement(customElement)
{ {
} }
@ -108,6 +109,11 @@ MarkdownCustomInline::Type MarkdownCustomInline::getType() const
return Type::CUSTOM_INLINE; return Type::CUSTOM_INLINE;
}; };
std::string MarkdownCustomInline::getTextContent() const
{
return mCustomElement->getOutputLeftDelimiter() + MarkdownElement::getTextContent() + mCustomElement->getOutputRightDelimiter();
}
MarkdownLink::MarkdownLink(const std::string& target) MarkdownLink::MarkdownLink(const std::string& target)
: mTarget(target) : mTarget(target)
{ {
@ -158,13 +164,19 @@ MarkdownMultilineQuote::Type MarkdownMultilineQuote::getType() const
} }
MarkdownCustomMultiLine::MarkdownCustomMultiLine(const std::string& tag, const std::string& delimiter) MarkdownCustomMultiLine::MarkdownCustomMultiLine(const std::string& tag, MarkdownCustomElementContext* customElement)
: mTag(tag), : mTag(tag),
mDelimiter(delimiter) mCustomElement(customElement)
{ {
} }
MarkdownCustomMultiLine::Type MarkdownCustomMultiLine::getType() const MarkdownCustomMultiLine::Type MarkdownCustomMultiLine::getType() const
{ {
return Type::CUSTOM_MULTILINE; return Type::CUSTOM_MULTILINE;
} }
std::string MarkdownCustomMultiLine::getTextContent() const
{
return mCustomElement->getOutputLeftDelimiter() + "\n" + MarkdownElement::getTextContent() + mCustomElement->getOutputRightDelimiter();
}

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "MarkdownElement.h" #include "MarkdownElement.h"
#include "MarkdownCustomElement.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
@ -120,12 +121,14 @@ public:
class MarkdownCustomInline : public MarkdownInlineElement class MarkdownCustomInline : public MarkdownInlineElement
{ {
public: public:
MarkdownCustomInline(const std::string& delimiter); MarkdownCustomInline(MarkdownCustomElementContext* customElement);
virtual ~MarkdownCustomInline() = default; virtual ~MarkdownCustomInline() = default;
Type getType() const override; Type getType() const override;
std::string getTextContent() const override;
private: private:
std::string mDelimiter; MarkdownCustomElementContext* mCustomElement{nullptr};
}; };
class MarkdownImage : public MarkdownInlineElement class MarkdownImage : public MarkdownInlineElement
@ -161,12 +164,14 @@ private:
class MarkdownCustomMultiLine : public MarkdownMultilineElement class MarkdownCustomMultiLine : public MarkdownMultilineElement
{ {
public: public:
MarkdownCustomMultiLine(const std::string& tag, const std::string& delimiter); MarkdownCustomMultiLine(const std::string& tag, MarkdownCustomElementContext* customElement);
virtual ~MarkdownCustomMultiLine() = default; virtual ~MarkdownCustomMultiLine() = default;
Type getType() const override; Type getType() const override;
std::string getTextContent() const override;
private: private:
std::string mTag; std::string mTag;
std::string mDelimiter; MarkdownCustomElementContext* mCustomElement{nullptr};
}; };

View file

@ -21,6 +21,12 @@ void MarkdownConverter::onBlockElement(MarkdownElementWithChildren* mdElement, H
html_quote->setText(child->getTextContent()); html_quote->setText(child->getTextContent());
htmlElement->addChild(std::move(html_quote)); htmlElement->addChild(std::move(html_quote));
} }
else if (child->getType() == MarkdownElement::Type::CUSTOM_INLINE)
{
auto html_text = std::make_unique<HtmlTextRun>();
html_text->setText(child->getTextContent());
htmlElement->addChild(std::move(html_text));
}
else if(child->getType() == MarkdownElement::Type::TEXT_SPAN) else if(child->getType() == MarkdownElement::Type::TEXT_SPAN)
{ {
auto html_text = std::make_unique<HtmlTextRun>(); auto html_text = std::make_unique<HtmlTextRun>();
@ -87,6 +93,12 @@ void MarkdownConverter::convert(MarkdownDocument* markdownDoc, HtmlElement* pare
html_quote->setText(md_element->getTextContent()); html_quote->setText(md_element->getTextContent());
parentElement->addChild(std::move(html_quote)); parentElement->addChild(std::move(html_quote));
} }
else if(md_element->getType() == MarkdownElement::Type::CUSTOM_MULTILINE)
{
auto html_text = std::make_unique<HtmlTextRun>();
html_text->setText("\n" + md_element->getTextContent() + "\n");
parentElement->addChild(std::move(html_text));
}
} }
} }

View file

@ -0,0 +1,79 @@
#pragma once
#include <string>
class MarkdownCustomElementContext
{
public:
MarkdownCustomElementContext(const std::string& delimiter)
: mDelimiter(delimiter)
{
}
virtual ~MarkdownCustomElementContext() = default;
void setReplacementDelimiters(const std::string& leftReplacement, const std::string& rightReplacement)
{
mReplacementLeftDelimiter = leftReplacement;
mReplacementRightDelimiter = rightReplacement;
if (mReplacementRightDelimiter.empty())
{
mReplacementRightDelimiter = mReplacementLeftDelimiter;
}
}
const std::string& getDelimiter() const
{
return mDelimiter;
}
const std::string& getOutputLeftDelimiter() const
{
if (!mReplacementLeftDelimiter.empty())
{
return mReplacementLeftDelimiter;
}
else
{
return mDelimiter;
}
}
const std::string& getOutputRightDelimiter() const
{
if (!mReplacementRightDelimiter.empty())
{
return mReplacementRightDelimiter;
}
else
{
return mDelimiter;
}
}
private:
std::string mReplacementLeftDelimiter;
std::string mReplacementRightDelimiter;
std::string mDelimiter;
};
class MarkdownCustomInlineElementContext : public MarkdownCustomElementContext
{
public:
MarkdownCustomInlineElementContext(const std::string& delimiter)
: MarkdownCustomElementContext(delimiter)
{
}
};
class MarkdownCustomMultilineElementContext : public MarkdownCustomElementContext
{
public:
MarkdownCustomMultilineElementContext(const std::string& delimiter)
: MarkdownCustomElementContext(delimiter)
{
}
};

View file

@ -3,6 +3,10 @@
#include "MarkdownElement.h" #include "MarkdownElement.h"
#include "MarkdownComponents.h" #include "MarkdownComponents.h"
MarkdownDocument::~MarkdownDocument()
{
}
void MarkdownDocument::addElement(std::unique_ptr<MarkdownElement> element) void MarkdownDocument::addElement(std::unique_ptr<MarkdownElement> element)
{ {
@ -19,14 +23,6 @@ MarkdownElement* MarkdownDocument::getElement(std::size_t idx) const
return mElements[idx].get(); 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*> MarkdownDocument::getAllLinks() const
{ {
std::vector<MarkdownLink*> links; std::vector<MarkdownLink*> links;
@ -49,3 +45,33 @@ std::vector<MarkdownLink*> MarkdownDocument::getAllLinks() const
} }
return links; return links;
} }
std::size_t MarkdownDocument::getNumMultilineContexts() const
{
return mCustomMultilineContexts.size();
}
std::size_t MarkdownDocument::getNumInlineContexts() const
{
return mCustomInlineContexts.size();
}
MarkdownCustomMultilineElementContext* MarkdownDocument::getMultilineContext(std::size_t idx) const
{
return mCustomMultilineContexts[idx].get();
}
MarkdownCustomInlineElementContext* MarkdownDocument::getInlineContext(std::size_t idx) const
{
return mCustomInlineContexts[idx].get();
}
void MarkdownDocument::registerCustomInlineElement(std::unique_ptr<MarkdownCustomInlineElementContext> context)
{
mCustomInlineContexts.push_back(std::move(context));
}
void MarkdownDocument::registerCustomMultilineElement(std::unique_ptr<MarkdownCustomMultilineElementContext> context)
{
mCustomMultilineContexts.push_back(std::move(context));
}

View file

@ -5,22 +5,38 @@
class MarkdownElement; class MarkdownElement;
class MarkdownLink; class MarkdownLink;
class MarkdownCustomMultilineElementContext;
class MarkdownCustomInlineElementContext;
class MarkdownDocument class MarkdownDocument
{ {
public: public:
~MarkdownDocument();
void addElement(std::unique_ptr<MarkdownElement> element); void addElement(std::unique_ptr<MarkdownElement> element);
std::size_t getNumElements() const; std::size_t getNumElements() const;
MarkdownElement* getElement(std::size_t idx) const; MarkdownElement* getElement(std::size_t idx) const;
void doLinkTargetSubstitution(const std::string& targetString, const std::string& replacementString);
std::vector<MarkdownLink*> getAllLinks() const; std::vector<MarkdownLink*> getAllLinks() const;
std::size_t getNumMultilineContexts() const;
std::size_t getNumInlineContexts() const;
MarkdownCustomMultilineElementContext* getMultilineContext(std::size_t idx) const;
MarkdownCustomInlineElementContext* getInlineContext(std::size_t idx) const;
void registerCustomInlineElement(std::unique_ptr<MarkdownCustomInlineElementContext> context);
void registerCustomMultilineElement(std::unique_ptr<MarkdownCustomMultilineElementContext> context);
private: private:
std::vector<std::unique_ptr<MarkdownElement> > mElements; std::vector<std::unique_ptr<MarkdownElement> > mElements;
std::vector<std::unique_ptr<MarkdownCustomMultilineElementContext> > mCustomMultilineContexts;
std::vector<std::unique_ptr<MarkdownCustomInlineElementContext> > mCustomInlineContexts;
}; };

View file

@ -5,7 +5,7 @@ void MarkdownElement::appendTextContent(const std::string& content)
mTextContent += content; mTextContent += content;
} }
const std::string& MarkdownElement::getTextContent() const std::string MarkdownElement::getTextContent() const
{ {
return mTextContent; return mTextContent;
} }

View file

@ -26,7 +26,7 @@ public:
void addLine(const std::string& line); void addLine(const std::string& line);
const std::string& getTextContent() const; virtual std::string getTextContent() const;
virtual Type getType() const = 0; virtual Type getType() const = 0;

View file

@ -2,6 +2,7 @@
#include "MarkdownDocument.h" #include "MarkdownDocument.h"
#include "MarkdownComponents.h" #include "MarkdownComponents.h"
#include "MarkdownCustomElement.h"
#include "Lexer.h" #include "Lexer.h"
#include "StringUtils.h" #include "StringUtils.h"
@ -13,8 +14,7 @@ static constexpr char HEADING_DELIMITER{'#'};
MarkdownParser::MarkdownParser() MarkdownParser::MarkdownParser()
{ {
mCustomMultilineDelimiters = {{"$$"}};
mCustomInlineDelimiters = {{"$"}};
} }
MarkdownParser::~MarkdownParser() MarkdownParser::~MarkdownParser()
@ -121,9 +121,9 @@ unsigned MarkdownParser::checkForCustomInline(const std::string& lineSection)
std::vector<std::string> hits; std::vector<std::string> hits;
unsigned hit_size{0}; unsigned hit_size{0};
for(unsigned idx=0; idx<mCustomInlineDelimiters.size(); idx++) for(unsigned idx=0; idx<mWorkingDocument->getNumInlineContexts(); idx++)
{ {
const auto delimiter = mCustomInlineDelimiters[idx]; const auto delimiter = mWorkingDocument->getInlineContext(idx)->getDelimiter();
if (Lexer::matchPattern(delimiter + "@" + delimiter, lineSection, '@', hits)) if (Lexer::matchPattern(delimiter + "@" + delimiter, lineSection, '@', hits))
{ {
if (hits.size() == 1) if (hits.size() == 1)
@ -132,7 +132,7 @@ unsigned MarkdownParser::checkForCustomInline(const std::string& lineSection)
onTextSpanFinished(); onTextSpanFinished();
auto element = std::make_unique<MarkdownCustomInline>(delimiter); auto element = std::make_unique<MarkdownCustomInline>(mWorkingDocument->getInlineContext(idx));
element->appendTextContent(content); element->appendTextContent(content);
addChildToWorkingElement(std::move(element)); addChildToWorkingElement(std::move(element));
@ -182,7 +182,7 @@ void MarkdownParser::processLine(const std::string& line)
{ {
auto paragraph = std::make_unique<MarkdownParagraph>(); auto paragraph = std::make_unique<MarkdownParagraph>();
mWorkingElement = paragraph.get(); mWorkingElement = paragraph.get();
mMarkdownDocument->addElement(std::move(paragraph)); mWorkingDocument->addElement(std::move(paragraph));
} }
if (mWorkingElement && mWorkingElement->getType() == MarkdownElement::Type::PARAGRAPH) if (mWorkingElement && mWorkingElement->getType() == MarkdownElement::Type::PARAGRAPH)
@ -239,9 +239,9 @@ bool MarkdownParser::startsWithMultiLineQuote(const std::string& line) const
int MarkdownParser::startsWithCustomMultilineBlock(const std::string& line) const int MarkdownParser::startsWithCustomMultilineBlock(const std::string& line) const
{ {
for(unsigned idx=0; idx<mCustomMultilineDelimiters.size(); idx++) for(unsigned idx=0; idx<mWorkingDocument->getNumMultilineContexts(); idx++)
{ {
if (StringUtils::startsWith(line, mCustomMultilineDelimiters[idx], true)) if (StringUtils::startsWith(line, mWorkingDocument->getMultilineContext(idx)->getDelimiter(), true))
{ {
return idx; return idx;
} }
@ -274,7 +274,7 @@ void MarkdownParser::onFoundMultiLineQuote(const std::string& line)
const auto tag = StringUtils::removeUpTo(line, MULTILINE_QUOTE_DELIMITER); const auto tag = StringUtils::removeUpTo(line, MULTILINE_QUOTE_DELIMITER);
auto quote = std::make_unique<MarkdownMultilineQuote>(tag); auto quote = std::make_unique<MarkdownMultilineQuote>(tag);
mWorkingElement = quote.get(); mWorkingElement = quote.get();
mMarkdownDocument->addElement(std::move(quote)); mWorkingDocument->addElement(std::move(quote));
} }
} }
@ -290,11 +290,11 @@ void MarkdownParser::onFoundCustomMultiLineBlock(const std::string& line, unsign
} }
else else
{ {
const auto delimiter = mCustomMultilineDelimiters[blockSlot]; const auto delimiter = mWorkingDocument->getMultilineContext(blockSlot)->getDelimiter();
const auto tag = StringUtils::removeUpTo(line, delimiter); const auto tag = StringUtils::removeUpTo(line, delimiter);
auto quote = std::make_unique<MarkdownCustomMultiLine>(tag, delimiter); auto quote = std::make_unique<MarkdownCustomMultiLine>(tag, mWorkingDocument->getMultilineContext(blockSlot));
mWorkingElement = quote.get(); mWorkingElement = quote.get();
mMarkdownDocument->addElement(std::move(quote)); mWorkingDocument->addElement(std::move(quote));
} }
} }
@ -317,7 +317,7 @@ void MarkdownParser::onFoundHeading(const std::string& line)
prefix += HEADING_DELIMITER; prefix += HEADING_DELIMITER;
} }
heading->appendTextContent(StringUtils::stripSurroundingWhitepsace(StringUtils::removeUpTo(line, prefix))); heading->appendTextContent(StringUtils::stripSurroundingWhitepsace(StringUtils::removeUpTo(line, prefix)));
mMarkdownDocument->addElement(std::move(heading)); mWorkingDocument->addElement(std::move(heading));
} }
} }
@ -340,7 +340,7 @@ void MarkdownParser::onFoundBulletItem(const std::string& line)
auto bullet_list = std::make_unique<MarkdownBulletList>(); auto bullet_list = std::make_unique<MarkdownBulletList>();
mWorkingBulletList = bullet_list.get(); mWorkingBulletList = bullet_list.get();
mMarkdownDocument->addElement(std::move(bullet_list)); mWorkingDocument->addElement(std::move(bullet_list));
auto bullet_item = std::make_unique<MarkdownBulletItem>(); auto bullet_item = std::make_unique<MarkdownBulletItem>();
mWorkingElement = bullet_item.get(); mWorkingElement = bullet_item.get();
@ -357,9 +357,9 @@ void MarkdownParser::onSectionFinished()
mWorkingTextSpan = nullptr; mWorkingTextSpan = nullptr;
} }
std::unique_ptr<MarkdownDocument> MarkdownParser::run(const std::string& content) void MarkdownParser::run(const std::string& content, MarkdownDocument* document)
{ {
mMarkdownDocument = std::make_unique<MarkdownDocument>(); mWorkingDocument = document;
std::stringstream ss(content); std::stringstream ss(content);
std::string line; std::string line;
@ -392,6 +392,13 @@ std::unique_ptr<MarkdownDocument> MarkdownParser::run(const std::string& content
processLine(line); processLine(line);
} }
} }
}
return std::move(mMarkdownDocument);
std::unique_ptr<MarkdownDocument> MarkdownParser::run(const std::string& content)
{
auto doc = std::make_unique<MarkdownDocument>();
run(content, doc.get());
return std::move(doc);
} }

View file

@ -8,6 +8,8 @@ class MarkdownDocument;
class MarkdownElement; class MarkdownElement;
class MarkdownInlineElement; class MarkdownInlineElement;
class MarkdownBulletList; class MarkdownBulletList;
class MarkdownCustomInlineElementContext;
class MarkdownCustomMultilineElementContext;
class MarkdownParser class MarkdownParser
{ {
@ -18,6 +20,8 @@ public:
std::unique_ptr<MarkdownDocument> run(const std::string& content); std::unique_ptr<MarkdownDocument> run(const std::string& content);
void run(const std::string& content, MarkdownDocument* document);
private: private:
void addChildToWorkingElement(std::unique_ptr<MarkdownInlineElement> child); void addChildToWorkingElement(std::unique_ptr<MarkdownInlineElement> child);
@ -45,8 +49,6 @@ private:
bool startsWithBulletItem(const std::string& line) const; bool startsWithBulletItem(const std::string& line) const;
unsigned mCustomDelimiterIndex{0}; unsigned mCustomDelimiterIndex{0};
std::vector<std::string> mCustomMultilineDelimiters;
std::vector<std::string> mCustomInlineDelimiters;
MarkdownElement* mWorkingElement{nullptr}; MarkdownElement* mWorkingElement{nullptr};
MarkdownBulletList* mWorkingBulletList{nullptr}; MarkdownBulletList* mWorkingBulletList{nullptr};
@ -54,5 +56,5 @@ private:
MarkdownInlineElement* mWorkingTextSpan{nullptr}; MarkdownInlineElement* mWorkingTextSpan{nullptr};
std::string mWorkingLine; std::string mWorkingLine;
std::unique_ptr<MarkdownDocument> mMarkdownDocument; MarkdownDocument* mWorkingDocument{nullptr};
}; };