add markdown dialect (WIP) & add strikethrough and underline font styles
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
multiline='true'
|
multiline='true'
|
||||||
size-func="gui.get_viewport()[1],40"
|
size-func="gui.get_viewport()[1],40"
|
||||||
gravity="bottom-left"
|
gravity="bottom-left"
|
||||||
|
markdown="true"
|
||||||
></textbox>
|
></textbox>
|
||||||
</container>
|
</container>
|
||||||
<container id="editorContainer" pos="0,60" color="#00000080"
|
<container id="editorContainer" pos="0,60" color="#00000080"
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
<textbox id='prompt'
|
<textbox id='prompt'
|
||||||
consumer='submit'
|
consumer='submit'
|
||||||
margin='0'
|
margin='0'
|
||||||
|
markdown="true"
|
||||||
gravity='bottom-left'
|
gravity='bottom-left'
|
||||||
size-func="gui.get_viewport()[1],40"
|
size-func="gui.get_viewport()[1],40"
|
||||||
onup="on_history_up()"
|
onup="on_history_up()"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
|
|
||||||
using namespace lua;
|
using namespace lua;
|
||||||
|
using namespace devtools;
|
||||||
|
|
||||||
static std::set<std::string_view> keywords {
|
static std::set<std::string_view> keywords {
|
||||||
"and", "break", "do", "else", "elseif", "end", "false", "for", "function",
|
"and", "break", "do", "else", "elseif", "end", "false", "for", "function",
|
||||||
|
|||||||
@@ -3,33 +3,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "devtools/syntax.hpp"
|
||||||
|
|
||||||
namespace lua {
|
namespace lua {
|
||||||
struct Location {
|
|
||||||
int pos;
|
|
||||||
int lineStart;
|
|
||||||
int line;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TokenTag {
|
|
||||||
KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING,
|
|
||||||
OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Token {
|
|
||||||
TokenTag tag;
|
|
||||||
std::string text;
|
|
||||||
Location start;
|
|
||||||
Location end;
|
|
||||||
|
|
||||||
Token(TokenTag tag, std::string text, Location start, Location end)
|
|
||||||
: tag(tag),
|
|
||||||
text(std::move(text)),
|
|
||||||
start(std::move(start)),
|
|
||||||
end(std::move(end)) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_lua_keyword(std::string_view view);
|
bool is_lua_keyword(std::string_view view);
|
||||||
|
|
||||||
std::vector<Token> tokenize(std::string_view file, std::string_view source);
|
std::vector<devtools::Token> tokenize(
|
||||||
|
std::string_view file, std::string_view source
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace devtools {
|
||||||
|
struct Location {
|
||||||
|
int pos;
|
||||||
|
int lineStart;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TokenTag {
|
||||||
|
KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING,
|
||||||
|
OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
TokenTag tag;
|
||||||
|
std::string text;
|
||||||
|
Location start;
|
||||||
|
Location end;
|
||||||
|
|
||||||
|
Token(TokenTag tag, std::string text, Location start, Location end)
|
||||||
|
: tag(tag),
|
||||||
|
text(std::move(text)),
|
||||||
|
start(std::move(start)),
|
||||||
|
end(std::move(end)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,28 +7,28 @@
|
|||||||
using namespace devtools;
|
using namespace devtools;
|
||||||
|
|
||||||
static std::unique_ptr<FontStylesScheme> build_styles(
|
static std::unique_ptr<FontStylesScheme> build_styles(
|
||||||
const std::vector<lua::Token>& tokens
|
const std::vector<devtools::Token>& tokens
|
||||||
) {
|
) {
|
||||||
|
using devtools::TokenTag;
|
||||||
FontStylesScheme styles {
|
FontStylesScheme styles {
|
||||||
{
|
{
|
||||||
{false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default
|
{false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default
|
||||||
{true, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword
|
{true, false, false, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword
|
||||||
{false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string
|
{false, false, false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string
|
||||||
{false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment
|
{false, false, false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment
|
||||||
{false, false, glm::vec4(0.4, 0.45f, 0.5f, 1)}, // self
|
{true, false, false, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected
|
||||||
{true, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected
|
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
for (int i = 0; i < tokens.size(); i++) {
|
for (int i = 0; i < tokens.size(); i++) {
|
||||||
const auto& token = tokens.at(i);
|
const auto& token = tokens.at(i);
|
||||||
if (token.tag != lua::TokenTag::KEYWORD &&
|
if (token.tag != TokenTag::KEYWORD &&
|
||||||
token.tag != lua::TokenTag::STRING &&
|
token.tag != TokenTag::STRING &&
|
||||||
token.tag != lua::TokenTag::INTEGER &&
|
token.tag != TokenTag::INTEGER &&
|
||||||
token.tag != lua::TokenTag::NUMBER &&
|
token.tag != TokenTag::NUMBER &&
|
||||||
token.tag != lua::TokenTag::COMMENT &&
|
token.tag != TokenTag::COMMENT &&
|
||||||
token.tag != lua::TokenTag::UNEXPECTED) {
|
token.tag != TokenTag::UNEXPECTED) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.start.pos > offset) {
|
if (token.start.pos > offset) {
|
||||||
@@ -38,12 +38,12 @@ static std::unique_ptr<FontStylesScheme> build_styles(
|
|||||||
offset = token.end.pos;
|
offset = token.end.pos;
|
||||||
int styleIndex;
|
int styleIndex;
|
||||||
switch (token.tag) {
|
switch (token.tag) {
|
||||||
case lua::TokenTag::KEYWORD: styleIndex = 1; break;
|
case TokenTag::KEYWORD: styleIndex = SyntaxStyles::KEYWORD; break;
|
||||||
case lua::TokenTag::STRING:
|
case TokenTag::STRING:
|
||||||
case lua::TokenTag::INTEGER:
|
case TokenTag::INTEGER:
|
||||||
case lua::TokenTag::NUMBER: styleIndex = 2; break;
|
case TokenTag::NUMBER: styleIndex = SyntaxStyles::LITERAL; break;
|
||||||
case lua::TokenTag::COMMENT: styleIndex = 3; break;
|
case TokenTag::COMMENT: styleIndex = SyntaxStyles::COMMENT; break;
|
||||||
case lua::TokenTag::UNEXPECTED: styleIndex = 5; break;
|
case TokenTag::UNEXPECTED: styleIndex = SyntaxStyles::ERROR; break;
|
||||||
default:
|
default:
|
||||||
styleIndex = 0;
|
styleIndex = 0;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
struct FontStylesScheme;
|
struct FontStylesScheme;
|
||||||
|
|
||||||
namespace devtools {
|
namespace devtools {
|
||||||
|
enum SyntaxStyles {
|
||||||
|
DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<FontStylesScheme> syntax_highlight(
|
std::unique_ptr<FontStylesScheme> syntax_highlight(
|
||||||
const std::string& lang, std::string_view source
|
const std::string& lang, std::string_view source
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ static inline void draw_text(
|
|||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& right,
|
const glm::vec3& right,
|
||||||
const glm::vec3& up,
|
const glm::vec3& up,
|
||||||
float glyphInterval,
|
float interval,
|
||||||
const FontStylesScheme* styles,
|
const FontStylesScheme* styles,
|
||||||
size_t styleMapOffset
|
size_t styleMapOffset
|
||||||
) {
|
) {
|
||||||
@@ -115,6 +115,7 @@ static inline void draw_text(
|
|||||||
uint next = MAX_CODEPAGES;
|
uint next = MAX_CODEPAGES;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
bool hasLines = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
for (size_t i = 0; i < text.length(); i++) {
|
for (size_t i = 0; i < text.length(); i++) {
|
||||||
@@ -123,6 +124,9 @@ static inline void draw_text(
|
|||||||
std::min(styles->map.size() - 1, i + styleMapOffset)
|
std::min(styles->map.size() - 1, i + styleMapOffset)
|
||||||
);
|
);
|
||||||
const FontStyle& style = styles->palette.at(styleIndex);
|
const FontStyle& style = styles->palette.at(styleIndex);
|
||||||
|
hasLines |= style.strikethrough;
|
||||||
|
hasLines |= style.underline;
|
||||||
|
|
||||||
if (!font.isPrintableChar(c)) {
|
if (!font.isPrintableChar(c)) {
|
||||||
x++;
|
x++;
|
||||||
continue;
|
continue;
|
||||||
@@ -131,14 +135,7 @@ static inline void draw_text(
|
|||||||
if (charpage == page){
|
if (charpage == page){
|
||||||
batch.texture(font.getPage(charpage));
|
batch.texture(font.getPage(charpage));
|
||||||
draw_glyph(
|
draw_glyph(
|
||||||
batch,
|
batch, pos, glm::vec2(x, y), c, right, up, interval, style
|
||||||
pos,
|
|
||||||
glm::vec2(x, y),
|
|
||||||
c,
|
|
||||||
right,
|
|
||||||
up,
|
|
||||||
glyphInterval,
|
|
||||||
style
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (charpage > page && charpage < next){
|
else if (charpage > page && charpage < next){
|
||||||
@@ -150,6 +147,31 @@ static inline void draw_text(
|
|||||||
next = MAX_CODEPAGES;
|
next = MAX_CODEPAGES;
|
||||||
x = 0;
|
x = 0;
|
||||||
} while (page < MAX_CODEPAGES);
|
} while (page < MAX_CODEPAGES);
|
||||||
|
|
||||||
|
if (!hasLines) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
batch.texture(font.getPage(0));
|
||||||
|
for (size_t i = 0; i < text.length(); i++) {
|
||||||
|
uint c = text[i];
|
||||||
|
size_t styleIndex = styles->map.at(
|
||||||
|
std::min(styles->map.size() - 1, i + styleMapOffset)
|
||||||
|
);
|
||||||
|
const FontStyle& style = styles->palette.at(styleIndex);
|
||||||
|
FontStyle lineStyle = style;
|
||||||
|
lineStyle.bold = true;
|
||||||
|
if (style.strikethrough) {
|
||||||
|
draw_glyph(
|
||||||
|
batch, pos, glm::vec2(x, y), '-', right, up, interval, lineStyle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (style.underline) {
|
||||||
|
draw_glyph(
|
||||||
|
batch, pos, glm::vec2(x, y), '_', right, up, interval, lineStyle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Texture* Font::getPage(int charpage) const {
|
const Texture* Font::getPage(int charpage) const {
|
||||||
|
|||||||
@@ -14,7 +14,25 @@ class Camera;
|
|||||||
struct FontStyle {
|
struct FontStyle {
|
||||||
bool bold = false;
|
bool bold = false;
|
||||||
bool italic = false;
|
bool italic = false;
|
||||||
|
bool strikethrough = false;
|
||||||
|
bool underline = false;
|
||||||
glm::vec4 color {1, 1, 1, 1};
|
glm::vec4 color {1, 1, 1, 1};
|
||||||
|
|
||||||
|
FontStyle() = default;
|
||||||
|
|
||||||
|
FontStyle(
|
||||||
|
bool bold,
|
||||||
|
bool italic,
|
||||||
|
bool strikethrough,
|
||||||
|
bool underline,
|
||||||
|
glm::vec4 color
|
||||||
|
)
|
||||||
|
: bold(bold),
|
||||||
|
italic(italic),
|
||||||
|
strikethrough(strikethrough),
|
||||||
|
underline(underline),
|
||||||
|
color(std::move(color)) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FontStylesScheme {
|
struct FontStylesScheme {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "graphics/core/Font.hpp"
|
#include "graphics/core/Font.hpp"
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
#include "../markdown.hpp"
|
||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
@@ -80,11 +81,16 @@ glm::vec2 Label::calcSize() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Label::setText(const std::wstring& text) {
|
void Label::setText(std::wstring text) {
|
||||||
|
if (isMarkdown()) {
|
||||||
|
auto [processedText, styles] = markdown::process(text, true);
|
||||||
|
text = std::move(processedText);
|
||||||
|
setStyles(std::move(styles));
|
||||||
|
}
|
||||||
if (text == this->text && !cache.resetFlag) {
|
if (text == this->text && !cache.resetFlag) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->text = text;
|
this->text = std::move(text);
|
||||||
cache.update(this->text, multiline, textWrap);
|
cache.update(this->text, multiline, textWrap);
|
||||||
|
|
||||||
if (cache.font && autoresize) {
|
if (cache.font && autoresize) {
|
||||||
@@ -242,6 +248,15 @@ bool Label::isTextWrapping() const {
|
|||||||
return textWrap;
|
return textWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Label::setMarkdown(bool flag) {
|
||||||
|
markdown = flag;
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Label::isMarkdown() const {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|
||||||
void Label::setStyles(std::unique_ptr<FontStylesScheme> styles) {
|
void Label::setStyles(std::unique_ptr<FontStylesScheme> styles) {
|
||||||
this->styles = std::move(styles);
|
this->styles = std::move(styles);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ namespace gui {
|
|||||||
/// @brief Auto resize label to fit text
|
/// @brief Auto resize label to fit text
|
||||||
bool autoresize = false;
|
bool autoresize = false;
|
||||||
|
|
||||||
|
/// @brief Enable text markdown
|
||||||
|
bool markdown = false;
|
||||||
|
|
||||||
std::unique_ptr<FontStylesScheme> styles;
|
std::unique_ptr<FontStylesScheme> styles;
|
||||||
public:
|
public:
|
||||||
Label(const std::string& text, std::string fontName="normal");
|
Label(const std::string& text, std::string fontName="normal");
|
||||||
@@ -60,7 +63,7 @@ namespace gui {
|
|||||||
|
|
||||||
virtual ~Label();
|
virtual ~Label();
|
||||||
|
|
||||||
virtual void setText(const std::wstring& text);
|
virtual void setText(std::wstring text);
|
||||||
const std::wstring& getText() const;
|
const std::wstring& getText() const;
|
||||||
|
|
||||||
virtual void setFontName(std::string name);
|
virtual void setFontName(std::string name);
|
||||||
@@ -113,6 +116,9 @@ namespace gui {
|
|||||||
virtual void setTextWrapping(bool flag);
|
virtual void setTextWrapping(bool flag);
|
||||||
virtual bool isTextWrapping() const;
|
virtual bool isTextWrapping() const;
|
||||||
|
|
||||||
|
virtual void setMarkdown(bool flag);
|
||||||
|
virtual bool isMarkdown() const;
|
||||||
|
|
||||||
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
|
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "window/Events.hpp"
|
#include "window/Events.hpp"
|
||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
|
#include "../markdown.hpp"
|
||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
@@ -191,7 +192,18 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
|
|||||||
|
|
||||||
void TextBox::refreshLabel() {
|
void TextBox::refreshLabel() {
|
||||||
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
|
||||||
label->setText(input.empty() && !hint.empty() ? hint : getText());
|
|
||||||
|
const auto& displayText = input.empty() && !hint.empty() ? hint : getText();
|
||||||
|
if (markdown) {
|
||||||
|
auto [processedText, styles] = markdown::process(displayText, !focused);
|
||||||
|
label->setText(std::move(processedText));
|
||||||
|
label->setStyles(std::move(styles));
|
||||||
|
} else {
|
||||||
|
label->setText(displayText);
|
||||||
|
if (syntax.empty()) {
|
||||||
|
label->setStyles(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showLineNumbers) {
|
if (showLineNumbers) {
|
||||||
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
|
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
|
||||||
@@ -846,3 +858,11 @@ void TextBox::setSyntax(const std::string& lang) {
|
|||||||
refreshSyntax();
|
refreshSyntax();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextBox::setMarkdown(bool flag) {
|
||||||
|
markdown = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextBox::isMarkdown() const {
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ namespace gui {
|
|||||||
bool editable = true;
|
bool editable = true;
|
||||||
bool autoresize = false;
|
bool autoresize = false;
|
||||||
bool showLineNumbers = false;
|
bool showLineNumbers = false;
|
||||||
|
bool markdown = false;
|
||||||
|
|
||||||
std::string syntax;
|
std::string syntax;
|
||||||
|
|
||||||
@@ -227,5 +228,8 @@ namespace gui {
|
|||||||
virtual void setOnDownPressed(const runnable &callback);
|
virtual void setOnDownPressed(const runnable &callback);
|
||||||
|
|
||||||
virtual void setSyntax(const std::string& lang);
|
virtual void setSyntax(const std::string& lang);
|
||||||
|
|
||||||
|
virtual void setMarkdown(bool flag);
|
||||||
|
virtual bool isMarkdown() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,6 +278,9 @@ static std::shared_ptr<UINode> readLabel(
|
|||||||
if (element.has("text-wrap")) {
|
if (element.has("text-wrap")) {
|
||||||
label->setTextWrapping(element.attr("text-wrap").asBool());
|
label->setTextWrapping(element.attr("text-wrap").asBool());
|
||||||
}
|
}
|
||||||
|
if (element.has("markdown")) {
|
||||||
|
label->setMarkdown(element.attr("markdown").asBool());
|
||||||
|
}
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +384,9 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
|
|||||||
if (element.has("line-numbers")) {
|
if (element.has("line-numbers")) {
|
||||||
textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
|
textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
|
||||||
}
|
}
|
||||||
|
if (element.has("markdown")) {
|
||||||
|
textbox->setMarkdown(element.attr("markdown").asBool());
|
||||||
|
}
|
||||||
if (element.has("consumer")) {
|
if (element.has("consumer")) {
|
||||||
textbox->setTextConsumer(scripting::create_wstring_consumer(
|
textbox->setTextConsumer(scripting::create_wstring_consumer(
|
||||||
reader.getEnvironment(),
|
reader.getEnvironment(),
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#include "markdown.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "graphics/core/Font.hpp"
|
||||||
|
|
||||||
|
using namespace markdown;
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
static inline void emit(
|
||||||
|
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
|
||||||
|
) {
|
||||||
|
ss << c;
|
||||||
|
styles.map.emplace_back(styles.palette.size()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
static inline void emit_md(
|
||||||
|
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
|
||||||
|
) {
|
||||||
|
ss << c;
|
||||||
|
styles.map.emplace_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
static inline void restyle(
|
||||||
|
CharT c,
|
||||||
|
FontStyle& style,
|
||||||
|
FontStylesScheme& styles,
|
||||||
|
std::basic_stringstream<CharT>& ss,
|
||||||
|
int& pos,
|
||||||
|
bool eraseMarkdown
|
||||||
|
) {
|
||||||
|
styles.palette.push_back(style);
|
||||||
|
if (!eraseMarkdown) {
|
||||||
|
emit_md(c, styles, ss);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
Result<CharT> process_markdown(
|
||||||
|
std::basic_string_view<CharT> source, bool eraseMarkdown
|
||||||
|
) {
|
||||||
|
std::basic_stringstream<CharT> ss;
|
||||||
|
FontStylesScheme styles {
|
||||||
|
// markdown default
|
||||||
|
{{false, false, false, false, glm::vec4(1,1,1,0.5f)}, {}},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
FontStyle style;
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < source.size()) {
|
||||||
|
CharT first = source[pos];
|
||||||
|
if (first == '\\') {
|
||||||
|
if (pos + 1 < source.size()) {
|
||||||
|
CharT second = source[++pos];
|
||||||
|
switch (second) {
|
||||||
|
case '*':
|
||||||
|
case '_':
|
||||||
|
case '~':
|
||||||
|
if (!eraseMarkdown) {
|
||||||
|
emit_md(first, styles, ss);
|
||||||
|
}
|
||||||
|
emit(second, styles, ss);
|
||||||
|
pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (first == '*') {
|
||||||
|
if (pos + 1 < source.size() && source[pos+1] == '*') {
|
||||||
|
pos++;
|
||||||
|
if (!eraseMarkdown)
|
||||||
|
emit_md(first, styles, ss);
|
||||||
|
style.bold = !style.bold;
|
||||||
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
style.italic = !style.italic;
|
||||||
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
|
continue;
|
||||||
|
} else if (first == '_' && pos + 1 < source.size() && source[pos+1] == '_') {
|
||||||
|
pos++;
|
||||||
|
if (!eraseMarkdown)
|
||||||
|
emit_md(first, styles, ss);
|
||||||
|
style.underline = !style.underline;
|
||||||
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
|
continue;
|
||||||
|
} else if (first == '~' && pos + 1 < source.size() && source[pos+1] == '~') {
|
||||||
|
pos++;
|
||||||
|
if (!eraseMarkdown)
|
||||||
|
emit_md(first, styles, ss);
|
||||||
|
style.strikethrough = !style.strikethrough;
|
||||||
|
restyle(first, style, styles, ss, pos, eraseMarkdown);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
emit(first, styles, ss);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return {ss.str(), std::make_unique<FontStylesScheme>(std::move(styles))};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<char> markdown::process(std::string_view source, bool eraseMarkdown) {
|
||||||
|
return process_markdown(source, eraseMarkdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<wchar_t> markdown::process(std::wstring_view source, bool eraseMarkdown) {
|
||||||
|
return process_markdown(source, eraseMarkdown);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct FontStylesScheme;
|
||||||
|
|
||||||
|
// VoxelCore Markdown dialect
|
||||||
|
|
||||||
|
namespace markdown {
|
||||||
|
|
||||||
|
template <typename CharT>
|
||||||
|
struct Result {
|
||||||
|
/// @brief Text with erased markdown
|
||||||
|
std::basic_string<CharT> text;
|
||||||
|
/// @brief Text styles scheme
|
||||||
|
std::unique_ptr<FontStylesScheme> styles;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result<char> process(std::string_view source, bool eraseMarkdown);
|
||||||
|
Result<wchar_t> process(std::wstring_view source, bool eraseMarkdown);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user