Some markdown processing.
This commit is contained in:
parent
31b479e9f6
commit
ec11529b9a
23 changed files with 677 additions and 135 deletions
59
src/web/markdown/MarkdownConverter.cpp
Normal file
59
src/web/markdown/MarkdownConverter.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "MarkdownConverter.h"
|
||||
|
||||
#include "HtmlDocument.h"
|
||||
#include "HtmlElement.h"
|
||||
#include "HtmlParagraphElement.h"
|
||||
#include "HtmlTextRun.h"
|
||||
|
||||
#include "MarkdownDocument.h"
|
||||
|
||||
std::unique_ptr<HtmlDocument> MarkdownConverter::convert(MarkdownDocument* markdownDoc) const
|
||||
{
|
||||
auto html_doc = std::make_unique<HtmlDocument>();
|
||||
|
||||
for(unsigned idx=0; idx<markdownDoc->getNumElements();idx++)
|
||||
{
|
||||
auto md_element = markdownDoc->getElement(idx);
|
||||
|
||||
if (md_element->getType() == MarkdownElement::Type::HEADING)
|
||||
{
|
||||
auto heading_level = dynamic_cast<MarkdownHeading*>(md_element)->getLevel();
|
||||
auto html_element = std::make_unique<HtmlHeadingElement>(heading_level);
|
||||
html_element->setText(md_element->getTextContent());
|
||||
|
||||
html_doc->addElementToBody(std::move(html_element));
|
||||
}
|
||||
else if(md_element->getType() == MarkdownElement::Type::PARAGRAPH)
|
||||
{
|
||||
auto html_p_element = std::make_unique<HtmlParagraphElement>();
|
||||
auto para_element = dynamic_cast<MarkdownParagraph*>(md_element);
|
||||
|
||||
for(unsigned idx=0; idx< para_element->getNumChildren(); idx++)
|
||||
{
|
||||
auto child = para_element->getChild(idx);
|
||||
if (child->getType() == MarkdownElement::Type::INLINE_QUOTE)
|
||||
{
|
||||
auto html_quote = std::make_unique<HtmlCodeElement>();
|
||||
html_quote->setText(child->getTextContent());
|
||||
html_p_element->addChild(std::move(html_quote));
|
||||
}
|
||||
else if(child->getType() == MarkdownElement::Type::TEXT_SPAN)
|
||||
{
|
||||
auto html_text = std::make_unique<HtmlTextRun>();
|
||||
html_text->setText(child->getTextContent());
|
||||
html_p_element->addChild(std::move(html_text));
|
||||
}
|
||||
}
|
||||
html_doc->addElementToBody(std::move(html_p_element));
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(html_doc);
|
||||
}
|
||||
|
13
src/web/markdown/MarkdownConverter.h
Normal file
13
src/web/markdown/MarkdownConverter.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class HtmlDocument;
|
||||
class MarkdownDocument;
|
||||
|
||||
class MarkdownConverter
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<HtmlDocument> convert(MarkdownDocument* markdownDoc) const;
|
||||
|
||||
};
|
0
src/web/markdown/MarkdownDocument.cpp
Normal file
0
src/web/markdown/MarkdownDocument.cpp
Normal file
167
src/web/markdown/MarkdownDocument.h
Normal file
167
src/web/markdown/MarkdownDocument.h
Normal file
|
@ -0,0 +1,167 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class MarkdownElement
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
HEADING,
|
||||
PARAGRAPH,
|
||||
TEXT_SPAN,
|
||||
INLINE_CODE,
|
||||
MULTILINE_CODE,
|
||||
INLINE_QUOTE,
|
||||
MULTILINE_QUOTE,
|
||||
INLINE_SPECIAL,
|
||||
MULTILINE_SPECIAL,
|
||||
LINK,
|
||||
IMAGE
|
||||
};
|
||||
|
||||
virtual ~MarkdownElement() = default;
|
||||
|
||||
void appendTextContent(const std::string& content)
|
||||
{
|
||||
mTextContent += content;
|
||||
}
|
||||
|
||||
const std::string& getTextContent() const
|
||||
{
|
||||
return mTextContent;
|
||||
}
|
||||
|
||||
virtual Type getType() const = 0;
|
||||
private:
|
||||
std::string mTextContent;
|
||||
};
|
||||
|
||||
class MarkdownInlineElement : public MarkdownElement
|
||||
{
|
||||
public:
|
||||
virtual ~MarkdownInlineElement() = default;
|
||||
};
|
||||
|
||||
class MarkdownTextSpan : public MarkdownInlineElement
|
||||
{
|
||||
public:
|
||||
virtual ~MarkdownTextSpan() = default;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::TEXT_SPAN;
|
||||
}
|
||||
};
|
||||
|
||||
class MarkdownParagraph : public MarkdownElement
|
||||
{
|
||||
public:
|
||||
virtual ~MarkdownParagraph() = default;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::PARAGRAPH;
|
||||
}
|
||||
|
||||
void addChild(std::unique_ptr<MarkdownInlineElement> child)
|
||||
{
|
||||
mChildren.push_back(std::move(child));
|
||||
}
|
||||
|
||||
std::size_t getNumChildren() const
|
||||
{
|
||||
return mChildren.size();
|
||||
}
|
||||
|
||||
MarkdownInlineElement* getChild(std::size_t idx) const
|
||||
{
|
||||
return mChildren[idx].get();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<MarkdownInlineElement> > mChildren;
|
||||
};
|
||||
|
||||
class MarkdownHeading : public MarkdownElement
|
||||
{
|
||||
public:
|
||||
MarkdownHeading(unsigned level)
|
||||
: mLevel(level)
|
||||
{
|
||||
|
||||
}
|
||||
virtual ~MarkdownHeading() = default;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::HEADING;
|
||||
}
|
||||
|
||||
unsigned getLevel() const
|
||||
{
|
||||
return mLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned mLevel{1};
|
||||
};
|
||||
|
||||
|
||||
|
||||
class MarkdownInlineQuote : public MarkdownInlineElement
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~MarkdownInlineQuote() = default;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::INLINE_QUOTE;
|
||||
}
|
||||
};
|
||||
|
||||
class MarkdownMultilineQuote : public MarkdownElement
|
||||
{
|
||||
public:
|
||||
MarkdownMultilineQuote(const std::string& tag)
|
||||
: mTag(tag)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~MarkdownMultilineQuote() = default;
|
||||
|
||||
Type getType() const override
|
||||
{
|
||||
return Type::MULTILINE_QUOTE;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mTag;
|
||||
};
|
||||
|
||||
|
||||
class MarkdownDocument
|
||||
{
|
||||
public:
|
||||
void addElement(std::unique_ptr<MarkdownElement> element)
|
||||
{
|
||||
mElements.push_back(std::move(element));
|
||||
}
|
||||
|
||||
std::size_t getNumElements() const
|
||||
{
|
||||
return mElements.size();
|
||||
}
|
||||
|
||||
MarkdownElement* getElement(std::size_t idx) const
|
||||
{
|
||||
return mElements[idx].get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<MarkdownElement> > mElements;
|
||||
};
|
||||
|
||||
|
|
@ -1,30 +1,231 @@
|
|||
#include "MarkdownParser.h"
|
||||
|
||||
#include "MarkdownDocument.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
MarkdownParser::MarkdownParser()
|
||||
: mHtmlDocument(HtmlDocument::Create())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MarkdownParser::processLine(const std::string& line)
|
||||
MarkdownParser::~MarkdownParser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MarkdownParser::run(const std::string& content)
|
||||
void MarkdownParser::onMultilineQuote()
|
||||
{
|
||||
std::stringstream ss(content);
|
||||
std::string line;
|
||||
while (std::getline(ss, line, '\n'))
|
||||
std::cout << "Adding multiline quote " << mDocumentContent << std::endl;
|
||||
auto quote = std::make_unique<MarkdownMultilineQuote>(mMultilineTag);
|
||||
quote->appendTextContent(mDocumentContent);
|
||||
mDocumentContent.clear();
|
||||
mDocumentState = DocumentState::NONE;
|
||||
mMarkdownDocument->addElement(std::move(quote));
|
||||
|
||||
onNewParagraph();
|
||||
}
|
||||
|
||||
void MarkdownParser::onInlineQuote()
|
||||
{
|
||||
std::cout << "Adding inline quote " << mLineContent << std::endl;
|
||||
|
||||
auto quote = std::make_unique<MarkdownInlineQuote>();
|
||||
quote->appendTextContent(mLineContent);
|
||||
mLineContent.clear();
|
||||
|
||||
mLineState = LineState::NONE;
|
||||
if(mWorkingParagraph)
|
||||
{
|
||||
processLine(line);
|
||||
mWorkingParagraph->addChild(std::move(quote));
|
||||
}
|
||||
}
|
||||
|
||||
HtmlDocumentPtr MarkdownParser::getHtml()
|
||||
void MarkdownParser::onHeading(unsigned level)
|
||||
{
|
||||
return std::move(mHtmlDocument);
|
||||
std::cout << "Adding heading: " << mLineContent << std::endl;
|
||||
|
||||
auto heading = std::make_unique<MarkdownHeading>(level);
|
||||
heading->appendTextContent(mLineContent);
|
||||
mMarkdownDocument->addElement(std::move(heading));
|
||||
}
|
||||
|
||||
void MarkdownParser::onNewParagraph()
|
||||
{
|
||||
if (mWorkingParagraph)
|
||||
{
|
||||
onTextSpan();
|
||||
|
||||
if (!mWorkingParagraph->getNumChildren() == 0)
|
||||
{
|
||||
std::cout << "Adding para to document" << std::endl;
|
||||
mMarkdownDocument->addElement(std::move(mWorkingParagraph));
|
||||
}
|
||||
}
|
||||
mWorkingParagraph = std::make_unique<MarkdownParagraph>();
|
||||
}
|
||||
|
||||
void MarkdownParser::onTextSpan()
|
||||
{
|
||||
mLineContent.clear();
|
||||
|
||||
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));
|
||||
mDocumentContent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned, bool> MarkdownParser::onTick(unsigned tickCount)
|
||||
{
|
||||
unsigned new_tick_count = tickCount;
|
||||
bool stop_line_processing = false;
|
||||
|
||||
if (tickCount == 2)
|
||||
{
|
||||
if (mDocumentState == DocumentState::IN_MULTILINEQUOTE)
|
||||
{
|
||||
onMultilineQuote();
|
||||
stop_line_processing = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
onNewParagraph();
|
||||
mLineState = LineState::IN_MULTILINE_TAG;
|
||||
new_tick_count = 0;
|
||||
mDocumentState = DocumentState::IN_MULTILINEQUOTE;
|
||||
}
|
||||
}
|
||||
else if(mLineState == LineState::IN_INLINEQUOTE)
|
||||
{
|
||||
if (mLineContent.empty())
|
||||
{
|
||||
mLineState = LineState::NONE;
|
||||
new_tick_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_tick_count = 0;
|
||||
onInlineQuote();
|
||||
}
|
||||
}
|
||||
else if(mDocumentState == DocumentState::IN_MULTILINEQUOTE)
|
||||
{
|
||||
new_tick_count++;
|
||||
mLineContent += '`';
|
||||
}
|
||||
else
|
||||
{
|
||||
new_tick_count++;
|
||||
mLineState = LineState::IN_INLINEQUOTE;
|
||||
}
|
||||
return {new_tick_count, stop_line_processing};
|
||||
}
|
||||
|
||||
void MarkdownParser::processLine()
|
||||
{
|
||||
mLineContent.clear();
|
||||
mLineState = LineState::NONE;
|
||||
|
||||
unsigned heading_level{0};
|
||||
unsigned tick_count{0};
|
||||
bool flushed_pre_inline = false;
|
||||
|
||||
for(auto c : mWorkingLine)
|
||||
{
|
||||
if (c == '`')
|
||||
{
|
||||
auto [ret_tick_count, stop_line_processing] = onTick(tick_count);
|
||||
tick_count = ret_tick_count;
|
||||
if(stop_line_processing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mLineState == LineState::IN_INLINEQUOTE)
|
||||
{
|
||||
if (!flushed_pre_inline)
|
||||
{
|
||||
std::cout << "Flushing pre-line " << std::endl;
|
||||
mDocumentContent += mLineContent;
|
||||
onTextSpan();
|
||||
flushed_pre_inline = true;
|
||||
}
|
||||
mLineContent += c;
|
||||
}
|
||||
else if (mDocumentState == DocumentState::IN_MULTILINEQUOTE)
|
||||
{
|
||||
mLineContent += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '#')
|
||||
{
|
||||
onNewParagraph();
|
||||
mLineState = LineState::IN_HEADING;
|
||||
heading_level++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLineContent += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mLineState == LineState::IN_HEADING)
|
||||
{
|
||||
onHeading(heading_level);
|
||||
}
|
||||
else if(mLineState == LineState::IN_MULTILINE_TAG)
|
||||
{
|
||||
mMultilineTag = mLineContent;
|
||||
}
|
||||
else if (mLineState == LineState::IN_INLINEQUOTE)
|
||||
{
|
||||
onTextSpan();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mLineContent.size() > 0)
|
||||
{
|
||||
mDocumentContent.append(mLineContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownParser::onEmptyLine()
|
||||
{
|
||||
onNewParagraph();
|
||||
}
|
||||
|
||||
std::unique_ptr<MarkdownDocument> MarkdownParser::run(const std::string& content)
|
||||
{
|
||||
mMarkdownDocument = std::make_unique<MarkdownDocument>();
|
||||
|
||||
std::stringstream ss(content);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(ss, line, '\n'))
|
||||
{
|
||||
if (line.empty())
|
||||
{
|
||||
onEmptyLine();
|
||||
continue;
|
||||
}
|
||||
mWorkingLine = line;
|
||||
processLine();
|
||||
}
|
||||
|
||||
onTextSpan();
|
||||
onNewParagraph();
|
||||
|
||||
return std::move(mMarkdownDocument);
|
||||
}
|
||||
|
|
|
@ -1,29 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "HtmlDocument.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class MarkdownDocument;
|
||||
class MarkdownParagraph;
|
||||
|
||||
class MarkdownParser
|
||||
{
|
||||
enum class DocumentState
|
||||
{
|
||||
None
|
||||
NONE,
|
||||
IN_MULTILINEQUOTE
|
||||
};
|
||||
|
||||
enum class LineState
|
||||
{
|
||||
None
|
||||
NONE,
|
||||
IN_HEADING,
|
||||
IN_INLINEQUOTE,
|
||||
IN_MULTILINE_TAG
|
||||
};
|
||||
|
||||
public:
|
||||
MarkdownParser();
|
||||
|
||||
HtmlDocumentPtr getHtml();
|
||||
~MarkdownParser();
|
||||
|
||||
void processLine(const std::string& line);
|
||||
|
||||
void run(const std::string& content);
|
||||
std::unique_ptr<MarkdownDocument> run(const std::string& content);
|
||||
|
||||
private:
|
||||
DocumentState mDocumentState {DocumentState::None};
|
||||
HtmlDocumentPtr mHtmlDocument;
|
||||
void processLine();
|
||||
|
||||
void onMultilineQuote();
|
||||
void onInlineQuote();
|
||||
void onHeading(unsigned level);
|
||||
|
||||
void onEmptyLine();
|
||||
void onNewParagraph();
|
||||
|
||||
void onTextSpan();
|
||||
|
||||
std::pair<unsigned, bool> onTick(unsigned tickCount);
|
||||
|
||||
std::string mWorkingLine;
|
||||
std::string mLineContent;
|
||||
std::string mDocumentContent;
|
||||
std::string mMultilineTag;
|
||||
|
||||
LineState mLineState {LineState::NONE};
|
||||
DocumentState mDocumentState {DocumentState::NONE};
|
||||
|
||||
std::unique_ptr<MarkdownParagraph> mWorkingParagraph{nullptr};
|
||||
std::unique_ptr<MarkdownDocument> mMarkdownDocument;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue