mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-11 17:17:42 +03:00
Added UIMarkdownView and Markdown class (integrated md4c into eepp).
Fixes in HTML rendering. Fix deadlock in Http pool shutdown.
This commit is contained in:
@@ -363,6 +363,12 @@
|
||||
"command": "${project_root}/bin/eepp-ui-richtext-debug",
|
||||
"name": "eepp-ui-richtext-debug",
|
||||
"working_dir": "${project_root}/bin"
|
||||
},
|
||||
{
|
||||
"args": "",
|
||||
"command": "${project_root}/bin/eepp-ui-markdownview-debug",
|
||||
"name": "eepp-ui-markdownview-debug",
|
||||
"working_dir": "${project_root}/bin"
|
||||
}
|
||||
],
|
||||
"var": {
|
||||
|
||||
@@ -774,6 +774,7 @@ class EE_API Http : NonCopyable {
|
||||
Mutex mThreadsMutex;
|
||||
bool mIsSSL;
|
||||
bool mHostSolved;
|
||||
std::atomic<bool> mShuttingDown{ false };
|
||||
URI mProxy;
|
||||
Mutex mCurRequestsMutex;
|
||||
std::unordered_map<Uint64, AsyncRequest*> mCurRequests;
|
||||
|
||||
17
include/eepp/ui/doc/markdownhelper.hpp
Normal file
17
include/eepp/ui/doc/markdownhelper.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef EE_UI_DOC_MARKDOWNHELPER_HPP
|
||||
#define EE_UI_DOC_MARKDOWNHELPER_HPP
|
||||
|
||||
#include <eepp/config.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace EE { namespace UI { namespace Doc {
|
||||
|
||||
class EE_API Markdown {
|
||||
public:
|
||||
static std::string toXHTML( std::string_view markdown );
|
||||
};
|
||||
|
||||
}}} // namespace EE::UI::Doc
|
||||
|
||||
#endif
|
||||
@@ -828,6 +828,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
TextDirection getTextDirection() const;
|
||||
|
||||
virtual void loadFromXmlNode( const pugi::xml_node& node );
|
||||
|
||||
void disableEditorFeatures( bool useDefaultStyle = true );
|
||||
|
||||
protected:
|
||||
struct LastXOffset {
|
||||
TextPosition position{ 0, 0 };
|
||||
@@ -1109,8 +1113,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
void resetPreviewColor();
|
||||
|
||||
void disableEditorFeatures();
|
||||
|
||||
void updateGlyphWidth();
|
||||
|
||||
Drawable* findIcon( const std::string& name );
|
||||
@@ -1190,6 +1192,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
}
|
||||
|
||||
bool setInternalFontSize( const Float& size );
|
||||
|
||||
virtual void onAutoSize();
|
||||
|
||||
virtual void onClassChange();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -110,6 +110,7 @@ enum UINodeType {
|
||||
UI_TYPE_NODELINK,
|
||||
UI_TYPE_TEXTSPAN,
|
||||
UI_TYPE_RICHTEXT,
|
||||
UI_TYPE_MARKDOWNVIEW,
|
||||
UI_TYPE_MODULES = 10000,
|
||||
UI_TYPE_TERMINAL = 10001,
|
||||
UI_TYPE_USER = 200000,
|
||||
|
||||
25
include/eepp/ui/uimarkdownview.hpp
Normal file
25
include/eepp/ui/uimarkdownview.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef EE_UI_UIMARKDOWNVIEW_HPP
|
||||
#define EE_UI_UIMARKDOWNVIEW_HPP
|
||||
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API UIMarkdownView : public UILinearLayout {
|
||||
public:
|
||||
static UIMarkdownView* New();
|
||||
|
||||
UIMarkdownView();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
void loadFromString( std::string_view markdown );
|
||||
|
||||
virtual void loadFromXmlNode( const pugi::xml_node& node );
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
14
premake4.lua
14
premake4.lua
@@ -763,6 +763,7 @@ function add_static_links()
|
||||
"oniguruma-static",
|
||||
"libwebp-static",
|
||||
"libpng-static",
|
||||
"md4c-static",
|
||||
}
|
||||
|
||||
if not _OPTIONS["without-mojoal"] then
|
||||
@@ -1321,6 +1322,13 @@ solution "eepp"
|
||||
build_base_configuration( "mojoal" )
|
||||
end
|
||||
|
||||
project "md4c-static"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
set_targetdir("libs/" .. os.get_real() .. "/thirdparty/")
|
||||
files { "src/thirdparty/md4c/**.c" }
|
||||
build_base_configuration( "md4c" )
|
||||
|
||||
project "libyaml-static"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
@@ -1580,6 +1588,12 @@ solution "eepp"
|
||||
files { "src/examples/ui_richtext/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-richtext", true )
|
||||
|
||||
project "eepp-ui-markdownview"
|
||||
set_kind()
|
||||
language "C++"
|
||||
files { "src/examples/ui_markdownview/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-markdownview", true )
|
||||
|
||||
project "eepp-richtext"
|
||||
set_kind()
|
||||
language "C++"
|
||||
|
||||
14
premake5.lua
14
premake5.lua
@@ -603,6 +603,7 @@ function add_static_links()
|
||||
"oniguruma-static",
|
||||
"libwebp-static",
|
||||
"libpng-static",
|
||||
"md4c-static",
|
||||
}
|
||||
|
||||
if not _OPTIONS["without-mojoal"] then
|
||||
@@ -1225,6 +1226,13 @@ workspace "eepp"
|
||||
filter { "options:windows-mingw-build", "options:arch=arm64" }
|
||||
incdirs { remote_sdl2_arm64_cross_tools_path .."/include/" }
|
||||
|
||||
project "md4c-static"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
files { "src/thirdparty/md4c/**.c" }
|
||||
build_base_configuration( "md4c" )
|
||||
target_dir_thirdparty()
|
||||
|
||||
project "libyaml-static"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
@@ -1464,6 +1472,12 @@ workspace "eepp"
|
||||
files { "src/examples/ui_richtext/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-richtext", true )
|
||||
|
||||
project "eepp-ui-markdownview"
|
||||
set_kind()
|
||||
language "C++"
|
||||
files { "src/examples/ui_markdownview/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-markdownview", true )
|
||||
|
||||
project "eepp-richtext"
|
||||
set_kind()
|
||||
language "C++"
|
||||
|
||||
@@ -111,7 +111,7 @@ LOCAL_C_INCLUDES := $(EEPP_C_INCLUDES)
|
||||
|
||||
LOCAL_SRC_FILES := $(foreach F, $(CODE_SRCS), $(addprefix $(dir $(F)),$(notdir $(wildcard $(LOCAL_PATH)/$(F)))))
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := freetype libpng libwebp pcre2 oniguruma harfbuzz sheenbidi
|
||||
LOCAL_STATIC_LIBRARIES := freetype libpng libwebp md4c pcre2 oniguruma harfbuzz sheenbidi
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := SDL2
|
||||
|
||||
@@ -367,6 +367,23 @@ LOCAL_SRC_FILES := $(foreach F, $(LIBYAML_SRCS), $(addprefix $(dir $(F)),$(not
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
#*************** LIBYAML ***************
|
||||
|
||||
#*************** MD4C ***************
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_PATH := $(EEPP_THIRD_PARTY_PATH)
|
||||
|
||||
LOCAL_MODULE := md4c
|
||||
|
||||
MD4C_SRCS := md4c/src/*.c
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/md4c
|
||||
LOCAL_CFLAGS := -Os
|
||||
|
||||
LOCAL_SRC_FILES := $(foreach F, $(MD4C_SRCS), $(addprefix $(dir $(F)),$(notdir $(wildcard $(LOCAL_PATH)/$(F)))))
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
#*************** MD4C ***************
|
||||
|
||||
#**************** SDL 2 ***************
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
|
||||
@@ -669,22 +669,7 @@ Http::Http( const std::string& host, unsigned short port, bool useSSL, URI proxy
|
||||
}
|
||||
|
||||
Http::~Http() {
|
||||
|
||||
{
|
||||
Lock l( mThreadsMutex );
|
||||
// First we wait to finish any request pending
|
||||
for ( auto&& itt : mThreads ) {
|
||||
itt->cancel();
|
||||
itt->wait();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock l( mThreadsMutex );
|
||||
for ( auto&& itt : mThreads ) {
|
||||
eeDelete( itt );
|
||||
}
|
||||
}
|
||||
mShuttingDown = true;
|
||||
|
||||
{
|
||||
Lock l( mCurRequestsMutex );
|
||||
@@ -692,6 +677,16 @@ Http::~Http() {
|
||||
req->cancel();
|
||||
}
|
||||
|
||||
{
|
||||
Lock l( mThreadsMutex );
|
||||
// First we wait to finish any request pending
|
||||
for ( auto& thread : mThreads ) {
|
||||
thread->cancel();
|
||||
thread->wait();
|
||||
eeDelete( thread );
|
||||
}
|
||||
}
|
||||
|
||||
// Then we destroy the last open connection
|
||||
HttpConnection* connection = mConnection;
|
||||
|
||||
@@ -1258,7 +1253,7 @@ void Http::AsyncRequest::run() {
|
||||
|
||||
mRunning = false;
|
||||
|
||||
if ( mFromLocalPool ) {
|
||||
if ( mFromLocalPool && !mHttp->mShuttingDown ) {
|
||||
mHttp->removeAsyncRequest( this );
|
||||
auto me = this;
|
||||
eeSAFE_DELETE( me );
|
||||
|
||||
@@ -218,7 +218,7 @@ void StyleSheetSpecification::registerDefaultProperties() {
|
||||
.setType( PropertyType::Color )
|
||||
.addAlias( "fontoutlinecolor" );
|
||||
registerProperty( "text-selection", "" ).setType( PropertyType::Bool );
|
||||
registerProperty( "text-align", "" );
|
||||
registerProperty( "text-align", "" ).addAlias( "align" );
|
||||
registerProperty( "icon", "" );
|
||||
registerProperty( "min-icon-size", "" ).setType( PropertyType::Vector2 );
|
||||
registerProperty( "src", "" ).setType( PropertyType::String );
|
||||
|
||||
18
src/eepp/ui/doc/markdownhelper.cpp
Normal file
18
src/eepp/ui/doc/markdownhelper.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <eepp/ui/doc/markdownhelper.hpp>
|
||||
#include <md4c/md4c-html.h>
|
||||
|
||||
namespace EE { namespace UI { namespace Doc {
|
||||
|
||||
static void process_output( const MD_CHAR* text, MD_SIZE size, void* userdata ) {
|
||||
std::string* out = static_cast<std::string*>( userdata );
|
||||
out->append( text, size );
|
||||
}
|
||||
|
||||
std::string Markdown::toXHTML( std::string_view markdown ) {
|
||||
std::string out;
|
||||
md_html( markdown.data(), markdown.size(), process_output, &out, MD_DIALECT_GITHUB,
|
||||
MD_HTML_FLAG_XHTML );
|
||||
return out;
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Doc
|
||||
@@ -26,6 +26,9 @@
|
||||
#include <eepp/window/input.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
using namespace EE::UI::Tools;
|
||||
|
||||
namespace EE { namespace UI {
|
||||
@@ -142,12 +145,16 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis
|
||||
mPreviewColor( Color::Transparent ),
|
||||
mKeyBindings( getInput() ),
|
||||
mFindLongestLineWidthUpdateFrequency( Seconds( 1 ) ) {
|
||||
mWidthPolicy = SizePolicy::Fixed;
|
||||
mHeightPolicy = SizePolicy::Fixed;
|
||||
mFlags |= UI_TAB_STOP | UI_OWNS_CHILDREN_POSITION | UI_SCROLLABLE;
|
||||
setTextSelection( true );
|
||||
setColorScheme( SyntaxColorScheme::getDefault() );
|
||||
refreshTag();
|
||||
mDocView.setOnVisibleLineCountChange(
|
||||
[this] { sendCommonEvent( Event::OnVisibleLinesCountChange ); } );
|
||||
mDocView.setOnVisibleLineCountChange( [this] {
|
||||
onAutoSize();
|
||||
sendCommonEvent( Event::OnVisibleLinesCountChange );
|
||||
} );
|
||||
mDocView.setOnFoldUnfoldCb(
|
||||
[this]( auto, auto ) { sendCommonEvent( Event::OnFoldUnfoldRange ); } );
|
||||
mVScrollBar = UIScrollBar::NewVertical();
|
||||
@@ -725,7 +732,7 @@ Uint32 UICodeEditor::onMessage( const NodeMessage* msg ) {
|
||||
return UIWidget::onMessage( msg );
|
||||
}
|
||||
|
||||
void UICodeEditor::disableEditorFeatures() {
|
||||
void UICodeEditor::disableEditorFeatures( bool useDefaultStyle ) {
|
||||
mShowLineNumber = false;
|
||||
mShowWhitespaces = false;
|
||||
mShowFoldingRegion = false;
|
||||
@@ -736,7 +743,7 @@ void UICodeEditor::disableEditorFeatures() {
|
||||
mMinimapEnabled = false;
|
||||
mFindReplaceEnabled = false;
|
||||
mLineBreakingColumn = 0;
|
||||
mUseDefaultStyle = true;
|
||||
mUseDefaultStyle = useDefaultStyle;
|
||||
}
|
||||
|
||||
Float UICodeEditor::getViewportWidth( bool forceVScroll, bool includeMinimap ) const {
|
||||
@@ -3386,6 +3393,10 @@ Float UICodeEditor::getGlyphWidth() const {
|
||||
}
|
||||
|
||||
void UICodeEditor::updateGlyphWidth() {
|
||||
if ( !mFont ) {
|
||||
mGlyphWidth = 0;
|
||||
return;
|
||||
}
|
||||
mGlyphWidth = mFont->getGlyph( '@', getCharacterSize(), false, false ).advance;
|
||||
mMouseWheelScroll = 3 * getLineHeight();
|
||||
invalidateLongestLineWidth();
|
||||
@@ -5510,4 +5521,38 @@ TextDirection UICodeEditor::getTextDirection() const {
|
||||
return mTextDirection;
|
||||
}
|
||||
|
||||
void UICodeEditor::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
beginAttributesTransaction();
|
||||
|
||||
UIWidget::loadFromXmlNode( node );
|
||||
|
||||
if ( !node.text().empty() ) {
|
||||
std::string_view str{ node.text().as_string() };
|
||||
if ( '\n' == str.back() )
|
||||
str = str.substr( 0, str.size() - 1 );
|
||||
mDoc->textInput( str );
|
||||
}
|
||||
|
||||
endAttributesTransaction();
|
||||
}
|
||||
|
||||
void UICodeEditor::onAutoSize() {
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
auto visibleLineCount = getDocumentView().getVisibleLinesCount();
|
||||
Float lineHeight = getLineHeight();
|
||||
Float height =
|
||||
lineHeight * visibleLineCount + getPixelsPadding().Top + getPixelsPadding().Bottom;
|
||||
setPixelsSize( getPixelsSize().getWidth(), height );
|
||||
}
|
||||
}
|
||||
|
||||
void UICodeEditor::onClassChange() {
|
||||
for ( const auto& name : mClasses ) {
|
||||
if ( String::startsWith( name, "language-" ) ) {
|
||||
auto langname = std::string_view{ name }.substr( 9 );
|
||||
setSyntaxDefinition( SyntaxDefinitionManager::instance()->findFromString( langname ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -282,6 +282,16 @@ bool UIImage::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
return false;
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::TextAlign: {
|
||||
std::string align = String::toLower( attribute.value() );
|
||||
if ( align == "center" )
|
||||
setHorizontalAlign( UI_HALIGN_CENTER );
|
||||
else if ( align == "left" )
|
||||
setHorizontalAlign( UI_HALIGN_LEFT );
|
||||
else if ( align == "right" )
|
||||
setHorizontalAlign( UI_HALIGN_RIGHT );
|
||||
break;
|
||||
}
|
||||
case PropertyId::Src: {
|
||||
std::string path( attribute.getValue() );
|
||||
bool ownIt;
|
||||
|
||||
41
src/eepp/ui/uimarkdownview.cpp
Normal file
41
src/eepp/ui/uimarkdownview.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <eepp/ui/doc/markdownhelper.hpp>
|
||||
#include <eepp/ui/uimarkdownview.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
using namespace EE::UI::Doc;
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
UIMarkdownView* UIMarkdownView::New() {
|
||||
return eeNew( UIMarkdownView, () );
|
||||
}
|
||||
|
||||
UIMarkdownView::UIMarkdownView() : UILinearLayout( "markdownview", UIOrientation::Vertical ) {
|
||||
mWidthPolicy = SizePolicy::MatchParent;
|
||||
mHeightPolicy = SizePolicy::WrapContent;
|
||||
}
|
||||
|
||||
Uint32 UIMarkdownView::getType() const {
|
||||
return UI_TYPE_MARKDOWNVIEW;
|
||||
}
|
||||
|
||||
bool UIMarkdownView::isType( const Uint32& type ) const {
|
||||
return UIMarkdownView::getType() == type || UILinearLayout::isType( type );
|
||||
}
|
||||
|
||||
void UIMarkdownView::loadFromString( std::string_view markdown ) {
|
||||
closeAllChildren();
|
||||
std::string xhtml = Markdown::toXHTML( markdown );
|
||||
getUISceneNode()->loadLayoutFromString( xhtml, this );
|
||||
}
|
||||
|
||||
void UIMarkdownView::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
UILinearLayout::loadFromXmlNode( node );
|
||||
if ( !node.text().empty() )
|
||||
loadFromString( node.text().as_string() );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <eepp/graphics/fontmanager.hpp>
|
||||
#include <eepp/graphics/text.hpp>
|
||||
#include <eepp/ui/css/propertydefinition.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
@@ -332,6 +333,25 @@ void UIRichText::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
UITextSpan* span = UITextSpan::New();
|
||||
span->setParent( this );
|
||||
span->loadFromXmlNode( child );
|
||||
} else if ( mTag == "pre" && String::iequals( child.name(), "code" ) ) {
|
||||
// Use a UICodeEditor for <pre><code>
|
||||
UICodeEditor* editor = UICodeEditor::New();
|
||||
if ( editor ) {
|
||||
editor->setParent( this );
|
||||
editor->loadFromXmlNode( child );
|
||||
editor->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent );
|
||||
editor->setLineWrapMode( LineWrapMode::Word );
|
||||
editor->setLineWrapType( LineWrapType::Viewport );
|
||||
editor->disableEditorFeatures( false );
|
||||
editor->setCursorVisible( false );
|
||||
editor->setAllowSelectingTextFromGutter( false );
|
||||
editor->setDisableCursorBlinkingAfterAMinuteOfInactivity( false );
|
||||
editor->setCursorBlinkTime( Time::Zero );
|
||||
editor->setShowLineNumber( false );
|
||||
editor->setShowFoldingRegion( false );
|
||||
editor->setLocked( true );
|
||||
|
||||
}
|
||||
} else {
|
||||
// Let parent logic load standard child widget
|
||||
UIWidget* widget = UIWidgetCreator::createFromName( child.name() );
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/window.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <eepp/ui/uilistbox.hpp>
|
||||
#include <eepp/ui/uilistview.hpp>
|
||||
#include <eepp/ui/uiloader.hpp>
|
||||
#include <eepp/ui/uimarkdownview.hpp>
|
||||
#include <eepp/ui/uimenubar.hpp>
|
||||
#include <eepp/ui/uinodelink.hpp>
|
||||
#include <eepp/ui/uiprogressbar.hpp>
|
||||
@@ -110,6 +111,7 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["imageviewer"] = Tools::UIImageViewer::New;
|
||||
registeredWidget["richtext"] = UIRichText::New;
|
||||
registeredWidget["textspan"] = UITextSpan::New;
|
||||
registeredWidget["markdownview"] = UIMarkdownView::New;
|
||||
|
||||
registeredWidget["hbox"] = UILinearLayout::NewHorizontal;
|
||||
registeredWidget["vbox"] = UILinearLayout::NewVertical;
|
||||
@@ -150,7 +152,7 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["ul"] = UILinearLayout::NewVerticalWidthMatchParent;
|
||||
registeredWidget["ol"] = UILinearLayout::NewVerticalWidthMatchParent;
|
||||
registeredWidget["li"] = UIRichText::NewListItem;
|
||||
registeredWidget["pre"] = UITextSpan::New;
|
||||
registeredWidget["pre"] = [] { return UIRichText::NewWithTag( "pre" ); };
|
||||
registeredWidget["img"] = [] { return UIImage::NewWithTag( "img" ); };
|
||||
|
||||
sBaseListCreated = true;
|
||||
|
||||
69
src/examples/ui_markdownview/ui_markdownview.cpp
Normal file
69
src/examples/ui_markdownview/ui_markdownview.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/ui/uimarkdownview.hpp>
|
||||
|
||||
EE_MAIN_FUNC int main( int, char** ) {
|
||||
UIApplication app( { 800, 600, "eepp - UIMarkdownView Example" } );
|
||||
|
||||
Log::instance()->setLiveWrite( true );
|
||||
Log::instance()->setLogToStdOut( true );
|
||||
|
||||
|
||||
app.getUI()->loadLayoutFromString( R"xml(
|
||||
<vbox layout_width="match_parent" layout_height="match_parent">
|
||||
<ScrollView lw="mp" lh="mp">
|
||||
<MarkdownView id="markdown_view" layout_width="match_parent" layout_height="wrap_content" padding="16dp">
|
||||
# Markdown Header 1
|
||||
## Markdown Header 2
|
||||
### Markdown Header 3
|
||||
|
||||
This is a **bold** text and this is an *italic* text.
|
||||
|
||||
* Item 1
|
||||
* Item 2
|
||||
* Item 3
|
||||
|
||||
1. Ordered 1
|
||||
2. Ordered 2
|
||||
|
||||
> This is a blockquote.
|
||||
|
||||
`inline code`
|
||||
|
||||
```cpp
|
||||
void main() {
|
||||
printf("Hello World");
|
||||
}
|
||||
```
|
||||
</MarkdownView>
|
||||
</ScrollView>
|
||||
</vbox>
|
||||
)xml" );
|
||||
|
||||
auto markdownView = app.getUI()->find<UIMarkdownView>( "markdown_view" );
|
||||
|
||||
app.getWindow()->getInput()->pushCallback( [markdownView]( InputEvent* event ) {
|
||||
switch ( event->Type ) {
|
||||
case InputEvent::FileDropped: {
|
||||
std::string file( event->file.file );
|
||||
std::string data;
|
||||
FileSystem::fileGet( file, data );
|
||||
markdownView->loadFromString( data );
|
||||
break;
|
||||
}
|
||||
case InputEvent::TextDropped: {
|
||||
markdownView->loadFromString( event->textdrop.text );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} );
|
||||
|
||||
app.getUI()->on( Event::KeyUp, [&app]( const Event* event ) {
|
||||
if ( event->asKeyEvent()->getKeyCode() == KEY_F11 ) {
|
||||
UIWidgetInspector::create( app.getUI() );
|
||||
}
|
||||
} );
|
||||
|
||||
return app.run();
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
EE_MAIN_FUNC int main( int, char** ) {
|
||||
UIApplication app( { 800, 600, "eepp - UIRichText Example" } );
|
||||
|
||||
app.getUI()->loadLayoutFromString( R"xml(
|
||||
<vbox layout_width="match_parent" layout_height="match_parent">
|
||||
<ScrollView lw="mp" lh="mp">
|
||||
|
||||
22
src/thirdparty/md4c/LICENSE.md
vendored
Normal file
22
src/thirdparty/md4c/LICENSE.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright © 2016-2024 Martin Mitáš
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the “Software”),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
2185
src/thirdparty/md4c/entity.c
vendored
Normal file
2185
src/thirdparty/md4c/entity.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
43
src/thirdparty/md4c/entity.h
vendored
Normal file
43
src/thirdparty/md4c/entity.h
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* MD4C: Markdown parser for C
|
||||
* (http://github.com/mity/md4c)
|
||||
*
|
||||
* Copyright (c) 2016-2024 Martin Mitáš
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MD4C_ENTITY_H
|
||||
#define MD4C_ENTITY_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/* Most entities are formed by single Unicode codepoint, few by two codepoints.
|
||||
* Single-codepoint entities have codepoints[1] set to zero. */
|
||||
typedef struct ENTITY_tag ENTITY;
|
||||
struct ENTITY_tag {
|
||||
const char* name;
|
||||
unsigned codepoints[2];
|
||||
};
|
||||
|
||||
const ENTITY* entity_lookup(const char* name, size_t name_size);
|
||||
|
||||
|
||||
#endif /* MD4C_ENTITY_H */
|
||||
567
src/thirdparty/md4c/md4c-html.c
vendored
Normal file
567
src/thirdparty/md4c/md4c-html.c
vendored
Normal file
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* MD4C: Markdown parser for C
|
||||
* (http://github.com/mity/md4c)
|
||||
*
|
||||
* Copyright (c) 2016-2024 Martin Mitáš
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "md4c-html.h"
|
||||
#include "entity.h"
|
||||
|
||||
|
||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199409L
|
||||
/* C89/90 or old compilers in general may not understand "inline". */
|
||||
#if defined __GNUC__
|
||||
#define inline __inline__
|
||||
#elif defined _MSC_VER
|
||||
#define inline __inline
|
||||
#else
|
||||
#define inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
typedef struct MD_HTML_tag MD_HTML;
|
||||
struct MD_HTML_tag {
|
||||
void (*process_output)(const MD_CHAR*, MD_SIZE, void*);
|
||||
void* userdata;
|
||||
unsigned flags;
|
||||
int image_nesting_level;
|
||||
char escape_map[256];
|
||||
};
|
||||
|
||||
#define NEED_HTML_ESC_FLAG 0x1
|
||||
#define NEED_URL_ESC_FLAG 0x2
|
||||
|
||||
|
||||
/*****************************************
|
||||
*** HTML rendering helper functions ***
|
||||
*****************************************/
|
||||
|
||||
#define ISDIGIT(ch) ('0' <= (ch) && (ch) <= '9')
|
||||
#define ISLOWER(ch) ('a' <= (ch) && (ch) <= 'z')
|
||||
#define ISUPPER(ch) ('A' <= (ch) && (ch) <= 'Z')
|
||||
#define ISALNUM(ch) (ISLOWER(ch) || ISUPPER(ch) || ISDIGIT(ch))
|
||||
|
||||
|
||||
static inline void
|
||||
render_verbatim(MD_HTML* r, const MD_CHAR* text, MD_SIZE size)
|
||||
{
|
||||
r->process_output(text, size, r->userdata);
|
||||
}
|
||||
|
||||
/* Keep this as a macro. Most compiler should then be smart enough to replace
|
||||
* the strlen() call with a compile-time constant if the string is a C literal. */
|
||||
#define RENDER_VERBATIM(r, verbatim) \
|
||||
render_verbatim((r), (verbatim), (MD_SIZE) (strlen(verbatim)))
|
||||
|
||||
|
||||
static void
|
||||
render_html_escaped(MD_HTML* r, const MD_CHAR* data, MD_SIZE size)
|
||||
{
|
||||
MD_OFFSET beg = 0;
|
||||
MD_OFFSET off = 0;
|
||||
|
||||
/* Some characters need to be escaped in normal HTML text. */
|
||||
#define NEED_HTML_ESC(ch) (r->escape_map[(unsigned char)(ch)] & NEED_HTML_ESC_FLAG)
|
||||
|
||||
while(1) {
|
||||
/* Optimization: Use some loop unrolling. */
|
||||
while(off + 3 < size && !NEED_HTML_ESC(data[off+0]) && !NEED_HTML_ESC(data[off+1])
|
||||
&& !NEED_HTML_ESC(data[off+2]) && !NEED_HTML_ESC(data[off+3]))
|
||||
off += 4;
|
||||
while(off < size && !NEED_HTML_ESC(data[off]))
|
||||
off++;
|
||||
|
||||
if(off > beg)
|
||||
render_verbatim(r, data + beg, off - beg);
|
||||
|
||||
if(off < size) {
|
||||
switch(data[off]) {
|
||||
case '&': RENDER_VERBATIM(r, "&"); break;
|
||||
case '<': RENDER_VERBATIM(r, "<"); break;
|
||||
case '>': RENDER_VERBATIM(r, ">"); break;
|
||||
case '"': RENDER_VERBATIM(r, """); break;
|
||||
}
|
||||
off++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
beg = off;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_url_escaped(MD_HTML* r, const MD_CHAR* data, MD_SIZE size)
|
||||
{
|
||||
static const MD_CHAR hex_chars[] = "0123456789ABCDEF";
|
||||
MD_OFFSET beg = 0;
|
||||
MD_OFFSET off = 0;
|
||||
|
||||
/* Some characters need to be escaped in URL attributes. */
|
||||
#define NEED_URL_ESC(ch) (r->escape_map[(unsigned char)(ch)] & NEED_URL_ESC_FLAG)
|
||||
|
||||
while(1) {
|
||||
while(off < size && !NEED_URL_ESC(data[off]))
|
||||
off++;
|
||||
if(off > beg)
|
||||
render_verbatim(r, data + beg, off - beg);
|
||||
|
||||
if(off < size) {
|
||||
char hex[3];
|
||||
|
||||
switch(data[off]) {
|
||||
case '&': RENDER_VERBATIM(r, "&"); break;
|
||||
default:
|
||||
hex[0] = '%';
|
||||
hex[1] = hex_chars[((unsigned)data[off] >> 4) & 0xf];
|
||||
hex[2] = hex_chars[((unsigned)data[off] >> 0) & 0xf];
|
||||
render_verbatim(r, hex, 3);
|
||||
break;
|
||||
}
|
||||
off++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
beg = off;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned
|
||||
hex_val(char ch)
|
||||
{
|
||||
if('0' <= ch && ch <= '9')
|
||||
return ch - '0';
|
||||
if('A' <= ch && ch <= 'Z')
|
||||
return ch - 'A' + 10;
|
||||
else
|
||||
return ch - 'a' + 10;
|
||||
}
|
||||
|
||||
static void
|
||||
render_utf8_codepoint(MD_HTML* r, unsigned codepoint,
|
||||
void (*fn_append)(MD_HTML*, const MD_CHAR*, MD_SIZE))
|
||||
{
|
||||
static const MD_CHAR utf8_replacement_char[] = { (char)0xef, (char)0xbf, (char)0xbd };
|
||||
|
||||
unsigned char utf8[4];
|
||||
size_t n;
|
||||
|
||||
if(codepoint <= 0x7f) {
|
||||
n = 1;
|
||||
utf8[0] = codepoint;
|
||||
} else if(codepoint <= 0x7ff) {
|
||||
n = 2;
|
||||
utf8[0] = 0xc0 | ((codepoint >> 6) & 0x1f);
|
||||
utf8[1] = 0x80 + ((codepoint >> 0) & 0x3f);
|
||||
} else if(codepoint <= 0xffff) {
|
||||
n = 3;
|
||||
utf8[0] = 0xe0 | ((codepoint >> 12) & 0xf);
|
||||
utf8[1] = 0x80 + ((codepoint >> 6) & 0x3f);
|
||||
utf8[2] = 0x80 + ((codepoint >> 0) & 0x3f);
|
||||
} else {
|
||||
n = 4;
|
||||
utf8[0] = 0xf0 | ((codepoint >> 18) & 0x7);
|
||||
utf8[1] = 0x80 + ((codepoint >> 12) & 0x3f);
|
||||
utf8[2] = 0x80 + ((codepoint >> 6) & 0x3f);
|
||||
utf8[3] = 0x80 + ((codepoint >> 0) & 0x3f);
|
||||
}
|
||||
|
||||
if(0 < codepoint && codepoint <= 0x10ffff)
|
||||
fn_append(r, (char*)utf8, (MD_SIZE)n);
|
||||
else
|
||||
fn_append(r, utf8_replacement_char, 3);
|
||||
}
|
||||
|
||||
/* Translate entity to its UTF-8 equivalent, or output the verbatim one
|
||||
* if such entity is unknown (or if the translation is disabled). */
|
||||
static void
|
||||
render_entity(MD_HTML* r, const MD_CHAR* text, MD_SIZE size,
|
||||
void (*fn_append)(MD_HTML*, const MD_CHAR*, MD_SIZE))
|
||||
{
|
||||
if(r->flags & MD_HTML_FLAG_VERBATIM_ENTITIES) {
|
||||
render_verbatim(r, text, size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We assume UTF-8 output is what is desired. */
|
||||
if(size > 3 && text[1] == '#') {
|
||||
unsigned codepoint = 0;
|
||||
|
||||
if(text[2] == 'x' || text[2] == 'X') {
|
||||
/* Hexadecimal entity (e.g. "�")). */
|
||||
MD_SIZE i;
|
||||
for(i = 3; i < size-1; i++)
|
||||
codepoint = 16 * codepoint + hex_val(text[i]);
|
||||
} else {
|
||||
/* Decimal entity (e.g. "&1234;") */
|
||||
MD_SIZE i;
|
||||
for(i = 2; i < size-1; i++)
|
||||
codepoint = 10 * codepoint + (text[i] - '0');
|
||||
}
|
||||
|
||||
render_utf8_codepoint(r, codepoint, fn_append);
|
||||
return;
|
||||
} else {
|
||||
/* Named entity (e.g. " "). */
|
||||
const ENTITY* ent;
|
||||
|
||||
ent = entity_lookup(text, size);
|
||||
if(ent != NULL) {
|
||||
render_utf8_codepoint(r, ent->codepoints[0], fn_append);
|
||||
if(ent->codepoints[1])
|
||||
render_utf8_codepoint(r, ent->codepoints[1], fn_append);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn_append(r, text, size);
|
||||
}
|
||||
|
||||
static void
|
||||
render_attribute(MD_HTML* r, const MD_ATTRIBUTE* attr,
|
||||
void (*fn_append)(MD_HTML*, const MD_CHAR*, MD_SIZE))
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; attr->substr_offsets[i] < attr->size; i++) {
|
||||
MD_TEXTTYPE type = attr->substr_types[i];
|
||||
MD_OFFSET off = attr->substr_offsets[i];
|
||||
MD_SIZE size = attr->substr_offsets[i+1] - off;
|
||||
const MD_CHAR* text = attr->text + off;
|
||||
|
||||
switch(type) {
|
||||
case MD_TEXT_NULLCHAR: render_utf8_codepoint(r, 0x0000, render_verbatim); break;
|
||||
case MD_TEXT_ENTITY: render_entity(r, text, size, fn_append); break;
|
||||
default: fn_append(r, text, size); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
render_open_ol_block(MD_HTML* r, const MD_BLOCK_OL_DETAIL* det)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
if(det->start == 1) {
|
||||
RENDER_VERBATIM(r, "<ol>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "<ol start=\"%u\">\n", det->start);
|
||||
RENDER_VERBATIM(r, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_li_block(MD_HTML* r, const MD_BLOCK_LI_DETAIL* det)
|
||||
{
|
||||
if(det->is_task) {
|
||||
RENDER_VERBATIM(r, "<li class=\"task-list-item\">"
|
||||
"<input type=\"checkbox\" class=\"task-list-item-checkbox\" disabled");
|
||||
if(det->task_mark == 'x' || det->task_mark == 'X')
|
||||
RENDER_VERBATIM(r, " checked");
|
||||
RENDER_VERBATIM(r, ">");
|
||||
} else {
|
||||
RENDER_VERBATIM(r, "<li>");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_code_block(MD_HTML* r, const MD_BLOCK_CODE_DETAIL* det)
|
||||
{
|
||||
RENDER_VERBATIM(r, "<pre><code");
|
||||
|
||||
/* If known, output the HTML 5 attribute class="language-LANGNAME". */
|
||||
if(det->lang.text != NULL) {
|
||||
RENDER_VERBATIM(r, " class=\"language-");
|
||||
render_attribute(r, &det->lang, render_html_escaped);
|
||||
RENDER_VERBATIM(r, "\"");
|
||||
}
|
||||
|
||||
RENDER_VERBATIM(r, ">");
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_td_block(MD_HTML* r, const MD_CHAR* cell_type, const MD_BLOCK_TD_DETAIL* det)
|
||||
{
|
||||
RENDER_VERBATIM(r, "<");
|
||||
RENDER_VERBATIM(r, cell_type);
|
||||
|
||||
switch(det->align) {
|
||||
case MD_ALIGN_LEFT: RENDER_VERBATIM(r, " align=\"left\">"); break;
|
||||
case MD_ALIGN_CENTER: RENDER_VERBATIM(r, " align=\"center\">"); break;
|
||||
case MD_ALIGN_RIGHT: RENDER_VERBATIM(r, " align=\"right\">"); break;
|
||||
default: RENDER_VERBATIM(r, ">"); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_a_span(MD_HTML* r, const MD_SPAN_A_DETAIL* det)
|
||||
{
|
||||
RENDER_VERBATIM(r, "<a href=\"");
|
||||
render_attribute(r, &det->href, render_url_escaped);
|
||||
|
||||
if(det->title.text != NULL) {
|
||||
RENDER_VERBATIM(r, "\" title=\"");
|
||||
render_attribute(r, &det->title, render_html_escaped);
|
||||
}
|
||||
|
||||
RENDER_VERBATIM(r, "\">");
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_img_span(MD_HTML* r, const MD_SPAN_IMG_DETAIL* det)
|
||||
{
|
||||
RENDER_VERBATIM(r, "<img src=\"");
|
||||
render_attribute(r, &det->src, render_url_escaped);
|
||||
|
||||
RENDER_VERBATIM(r, "\" alt=\"");
|
||||
}
|
||||
|
||||
static void
|
||||
render_close_img_span(MD_HTML* r, const MD_SPAN_IMG_DETAIL* det)
|
||||
{
|
||||
if(det->title.text != NULL) {
|
||||
RENDER_VERBATIM(r, "\" title=\"");
|
||||
render_attribute(r, &det->title, render_html_escaped);
|
||||
}
|
||||
|
||||
RENDER_VERBATIM(r, (r->flags & MD_HTML_FLAG_XHTML) ? "\" />" : "\">");
|
||||
}
|
||||
|
||||
static void
|
||||
render_open_wikilink_span(MD_HTML* r, const MD_SPAN_WIKILINK_DETAIL* det)
|
||||
{
|
||||
RENDER_VERBATIM(r, "<x-wikilink data-target=\"");
|
||||
render_attribute(r, &det->target, render_html_escaped);
|
||||
|
||||
RENDER_VERBATIM(r, "\">");
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
*** HTML renderer implementation ***
|
||||
**************************************/
|
||||
|
||||
static int
|
||||
enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
static const MD_CHAR* head[6] = { "<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>" };
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
|
||||
switch(type) {
|
||||
case MD_BLOCK_DOC: /* noop */ break;
|
||||
case MD_BLOCK_QUOTE: RENDER_VERBATIM(r, "<blockquote>\n"); break;
|
||||
case MD_BLOCK_UL: RENDER_VERBATIM(r, "<ul>\n"); break;
|
||||
case MD_BLOCK_OL: render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL*)detail); break;
|
||||
case MD_BLOCK_LI: render_open_li_block(r, (const MD_BLOCK_LI_DETAIL*)detail); break;
|
||||
case MD_BLOCK_HR: RENDER_VERBATIM(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr />\n" : "<hr>\n"); break;
|
||||
case MD_BLOCK_H: RENDER_VERBATIM(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
|
||||
case MD_BLOCK_CODE: render_open_code_block(r, (const MD_BLOCK_CODE_DETAIL*) detail); break;
|
||||
case MD_BLOCK_HTML: /* noop */ break;
|
||||
case MD_BLOCK_P: RENDER_VERBATIM(r, "<p>"); break;
|
||||
case MD_BLOCK_TABLE: RENDER_VERBATIM(r, "<table>\n"); break;
|
||||
case MD_BLOCK_THEAD: RENDER_VERBATIM(r, "<thead>\n"); break;
|
||||
case MD_BLOCK_TBODY: RENDER_VERBATIM(r, "<tbody>\n"); break;
|
||||
case MD_BLOCK_TR: RENDER_VERBATIM(r, "<tr>\n"); break;
|
||||
case MD_BLOCK_TH: render_open_td_block(r, "th", (MD_BLOCK_TD_DETAIL*)detail); break;
|
||||
case MD_BLOCK_TD: render_open_td_block(r, "td", (MD_BLOCK_TD_DETAIL*)detail); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
static const MD_CHAR* head[6] = { "</h1>\n", "</h2>\n", "</h3>\n", "</h4>\n", "</h5>\n", "</h6>\n" };
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
|
||||
switch(type) {
|
||||
case MD_BLOCK_DOC: /*noop*/ break;
|
||||
case MD_BLOCK_QUOTE: RENDER_VERBATIM(r, "</blockquote>\n"); break;
|
||||
case MD_BLOCK_UL: RENDER_VERBATIM(r, "</ul>\n"); break;
|
||||
case MD_BLOCK_OL: RENDER_VERBATIM(r, "</ol>\n"); break;
|
||||
case MD_BLOCK_LI: RENDER_VERBATIM(r, "</li>\n"); break;
|
||||
case MD_BLOCK_HR: /*noop*/ break;
|
||||
case MD_BLOCK_H: RENDER_VERBATIM(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
|
||||
case MD_BLOCK_CODE: RENDER_VERBATIM(r, "</code></pre>\n"); break;
|
||||
case MD_BLOCK_HTML: /* noop */ break;
|
||||
case MD_BLOCK_P: RENDER_VERBATIM(r, "</p>\n"); break;
|
||||
case MD_BLOCK_TABLE: RENDER_VERBATIM(r, "</table>\n"); break;
|
||||
case MD_BLOCK_THEAD: RENDER_VERBATIM(r, "</thead>\n"); break;
|
||||
case MD_BLOCK_TBODY: RENDER_VERBATIM(r, "</tbody>\n"); break;
|
||||
case MD_BLOCK_TR: RENDER_VERBATIM(r, "</tr>\n"); break;
|
||||
case MD_BLOCK_TH: RENDER_VERBATIM(r, "</th>\n"); break;
|
||||
case MD_BLOCK_TD: RENDER_VERBATIM(r, "</td>\n"); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
int inside_img = (r->image_nesting_level > 0);
|
||||
|
||||
/* We are inside a Markdown image label. Markdown allows to use any emphasis
|
||||
* and other rich contents in that context similarly as in any link label.
|
||||
*
|
||||
* However, unlike in the case of links (where that contents becomescontents
|
||||
* of the <a>...</a> tag), in the case of images the contents is supposed to
|
||||
* fall into the attribute alt: <img alt="...">.
|
||||
*
|
||||
* In that context we naturally cannot output nested HTML tags. So lets
|
||||
* suppress them and only output the plain text (i.e. what falls into text()
|
||||
* callback).
|
||||
*
|
||||
* CommonMark specification declares this a recommended practice for HTML
|
||||
* output.
|
||||
*/
|
||||
if(type == MD_SPAN_IMG)
|
||||
r->image_nesting_level++;
|
||||
if(inside_img)
|
||||
return 0;
|
||||
|
||||
switch(type) {
|
||||
case MD_SPAN_EM: RENDER_VERBATIM(r, "<em>"); break;
|
||||
case MD_SPAN_STRONG: RENDER_VERBATIM(r, "<strong>"); break;
|
||||
case MD_SPAN_U: RENDER_VERBATIM(r, "<u>"); break;
|
||||
case MD_SPAN_A: render_open_a_span(r, (MD_SPAN_A_DETAIL*) detail); break;
|
||||
case MD_SPAN_IMG: render_open_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); break;
|
||||
case MD_SPAN_CODE: RENDER_VERBATIM(r, "<code>"); break;
|
||||
case MD_SPAN_DEL: RENDER_VERBATIM(r, "<del>"); break;
|
||||
case MD_SPAN_LATEXMATH: RENDER_VERBATIM(r, "<x-equation>"); break;
|
||||
case MD_SPAN_LATEXMATH_DISPLAY: RENDER_VERBATIM(r, "<x-equation type=\"display\">"); break;
|
||||
case MD_SPAN_WIKILINK: render_open_wikilink_span(r, (MD_SPAN_WIKILINK_DETAIL*) detail); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata)
|
||||
{
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
|
||||
if(type == MD_SPAN_IMG)
|
||||
r->image_nesting_level--;
|
||||
if(r->image_nesting_level > 0)
|
||||
return 0;
|
||||
|
||||
switch(type) {
|
||||
case MD_SPAN_EM: RENDER_VERBATIM(r, "</em>"); break;
|
||||
case MD_SPAN_STRONG: RENDER_VERBATIM(r, "</strong>"); break;
|
||||
case MD_SPAN_U: RENDER_VERBATIM(r, "</u>"); break;
|
||||
case MD_SPAN_A: RENDER_VERBATIM(r, "</a>"); break;
|
||||
case MD_SPAN_IMG: render_close_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); break;
|
||||
case MD_SPAN_CODE: RENDER_VERBATIM(r, "</code>"); break;
|
||||
case MD_SPAN_DEL: RENDER_VERBATIM(r, "</del>"); break;
|
||||
case MD_SPAN_LATEXMATH: /*fall through*/
|
||||
case MD_SPAN_LATEXMATH_DISPLAY: RENDER_VERBATIM(r, "</x-equation>"); break;
|
||||
case MD_SPAN_WIKILINK: RENDER_VERBATIM(r, "</x-wikilink>"); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata)
|
||||
{
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
|
||||
switch(type) {
|
||||
case MD_TEXT_NULLCHAR: render_utf8_codepoint(r, 0x0000, render_verbatim); break;
|
||||
case MD_TEXT_BR: RENDER_VERBATIM(r, (r->image_nesting_level == 0
|
||||
? ((r->flags & MD_HTML_FLAG_XHTML) ? "<br />\n" : "<br>\n")
|
||||
: " "));
|
||||
break;
|
||||
case MD_TEXT_SOFTBR: RENDER_VERBATIM(r, (r->image_nesting_level == 0 ? "\n" : " ")); break;
|
||||
case MD_TEXT_HTML: render_verbatim(r, text, size); break;
|
||||
case MD_TEXT_ENTITY: render_entity(r, text, size, render_html_escaped); break;
|
||||
default: render_html_escaped(r, text, size); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
debug_log_callback(const char* msg, void* userdata)
|
||||
{
|
||||
MD_HTML* r = (MD_HTML*) userdata;
|
||||
if(r->flags & MD_HTML_FLAG_DEBUG)
|
||||
fprintf(stderr, "MD4C: %s\n", msg);
|
||||
}
|
||||
|
||||
int
|
||||
md_html(const MD_CHAR* input, MD_SIZE input_size,
|
||||
void (*process_output)(const MD_CHAR*, MD_SIZE, void*),
|
||||
void* userdata, unsigned parser_flags, unsigned renderer_flags)
|
||||
{
|
||||
MD_HTML render = { process_output, userdata, renderer_flags, 0, { 0 } };
|
||||
int i;
|
||||
|
||||
MD_PARSER parser = {
|
||||
0,
|
||||
parser_flags,
|
||||
enter_block_callback,
|
||||
leave_block_callback,
|
||||
enter_span_callback,
|
||||
leave_span_callback,
|
||||
text_callback,
|
||||
debug_log_callback,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Build map of characters which need escaping. */
|
||||
for(i = 0; i < 256; i++) {
|
||||
unsigned char ch = (unsigned char) i;
|
||||
|
||||
if(strchr("\"&<>", ch) != NULL)
|
||||
render.escape_map[i] |= NEED_HTML_ESC_FLAG;
|
||||
|
||||
if(!ISALNUM(ch) && strchr("~-_.+!*(),%#@?=;:/,+$", ch) == NULL)
|
||||
render.escape_map[i] |= NEED_URL_ESC_FLAG;
|
||||
}
|
||||
|
||||
/* Consider skipping UTF-8 byte order mark (BOM). */
|
||||
if(renderer_flags & MD_HTML_FLAG_SKIP_UTF8_BOM && sizeof(MD_CHAR) == 1) {
|
||||
static const MD_CHAR bom[3] = { (char)0xef, (char)0xbb, (char)0xbf };
|
||||
if(input_size >= sizeof(bom) && memcmp(input, bom, sizeof(bom)) == 0) {
|
||||
input += sizeof(bom);
|
||||
input_size -= sizeof(bom);
|
||||
}
|
||||
}
|
||||
|
||||
return md_parse(input, input_size, &parser, (void*) &render);
|
||||
}
|
||||
|
||||
68
src/thirdparty/md4c/md4c-html.h
vendored
Normal file
68
src/thirdparty/md4c/md4c-html.h
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* MD4C: Markdown parser for C
|
||||
* (http://github.com/mity/md4c)
|
||||
*
|
||||
* Copyright (c) 2016-2024 Martin Mitáš
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MD4C_HTML_H
|
||||
#define MD4C_HTML_H
|
||||
|
||||
#include "md4c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* If set, debug output from md_parse() is sent to stderr. */
|
||||
#define MD_HTML_FLAG_DEBUG 0x0001
|
||||
#define MD_HTML_FLAG_VERBATIM_ENTITIES 0x0002
|
||||
#define MD_HTML_FLAG_SKIP_UTF8_BOM 0x0004
|
||||
#define MD_HTML_FLAG_XHTML 0x0008
|
||||
|
||||
|
||||
/* Render Markdown into HTML.
|
||||
*
|
||||
* Note only contents of <body> tag is generated. Caller must generate
|
||||
* HTML header/footer manually before/after calling md_html().
|
||||
*
|
||||
* Params input and input_size specify the Markdown input.
|
||||
* Callback process_output() gets called with chunks of HTML output.
|
||||
* (Typical implementation may just output the bytes to a file or append to
|
||||
* some buffer).
|
||||
* Param userdata is just propagated back to process_output() callback.
|
||||
* Param parser_flags are flags from md4c.h propagated to md_parse().
|
||||
* Param render_flags is bitmask of MD_HTML_FLAG_xxxx.
|
||||
*
|
||||
* Returns -1 on error (if md_parse() fails.)
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int md_html(const MD_CHAR* input, MD_SIZE input_size,
|
||||
void (*process_output)(const MD_CHAR*, MD_SIZE, void*),
|
||||
void* userdata, unsigned parser_flags, unsigned renderer_flags);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" { */
|
||||
#endif
|
||||
|
||||
#endif /* MD4C_HTML_H */
|
||||
6492
src/thirdparty/md4c/md4c.c
vendored
Normal file
6492
src/thirdparty/md4c/md4c.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
407
src/thirdparty/md4c/md4c.h
vendored
Normal file
407
src/thirdparty/md4c/md4c.h
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* MD4C: Markdown parser for C
|
||||
* (http://github.com/mity/md4c)
|
||||
*
|
||||
* Copyright (c) 2016-2024 Martin Mitáš
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MD4C_H
|
||||
#define MD4C_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined MD4C_USE_UTF16
|
||||
/* Magic to support UTF-16. Note that in order to use it, you have to define
|
||||
* the macro MD4C_USE_UTF16 both when building MD4C as well as when
|
||||
* including this header in your code. */
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
typedef WCHAR MD_CHAR;
|
||||
#else
|
||||
#error MD4C_USE_UTF16 is only supported on Windows.
|
||||
#endif
|
||||
#else
|
||||
typedef char MD_CHAR;
|
||||
#endif
|
||||
|
||||
typedef unsigned MD_SIZE;
|
||||
typedef unsigned MD_OFFSET;
|
||||
|
||||
|
||||
/* Block represents a part of document hierarchy structure like a paragraph
|
||||
* or list item.
|
||||
*/
|
||||
typedef enum MD_BLOCKTYPE {
|
||||
/* <body>...</body> */
|
||||
MD_BLOCK_DOC = 0,
|
||||
|
||||
/* <blockquote>...</blockquote> */
|
||||
MD_BLOCK_QUOTE,
|
||||
|
||||
/* <ul>...</ul>
|
||||
* Detail: Structure MD_BLOCK_UL_DETAIL. */
|
||||
MD_BLOCK_UL,
|
||||
|
||||
/* <ol>...</ol>
|
||||
* Detail: Structure MD_BLOCK_OL_DETAIL. */
|
||||
MD_BLOCK_OL,
|
||||
|
||||
/* <li>...</li>
|
||||
* Detail: Structure MD_BLOCK_LI_DETAIL. */
|
||||
MD_BLOCK_LI,
|
||||
|
||||
/* <hr> */
|
||||
MD_BLOCK_HR,
|
||||
|
||||
/* <h1>...</h1> (for levels up to 6)
|
||||
* Detail: Structure MD_BLOCK_H_DETAIL. */
|
||||
MD_BLOCK_H,
|
||||
|
||||
/* <pre><code>...</code></pre>
|
||||
* Note the text lines within code blocks are terminated with '\n'
|
||||
* instead of explicit MD_TEXT_BR. */
|
||||
MD_BLOCK_CODE,
|
||||
|
||||
/* Raw HTML block. This itself does not correspond to any particular HTML
|
||||
* tag. The contents of it _is_ raw HTML source intended to be put
|
||||
* in verbatim form to the HTML output. */
|
||||
MD_BLOCK_HTML,
|
||||
|
||||
/* <p>...</p> */
|
||||
MD_BLOCK_P,
|
||||
|
||||
/* <table>...</table> and its contents.
|
||||
* Detail: Structure MD_BLOCK_TABLE_DETAIL (for MD_BLOCK_TABLE),
|
||||
* structure MD_BLOCK_TD_DETAIL (for MD_BLOCK_TH and MD_BLOCK_TD)
|
||||
* Note all of these are used only if extension MD_FLAG_TABLES is enabled. */
|
||||
MD_BLOCK_TABLE,
|
||||
MD_BLOCK_THEAD,
|
||||
MD_BLOCK_TBODY,
|
||||
MD_BLOCK_TR,
|
||||
MD_BLOCK_TH,
|
||||
MD_BLOCK_TD
|
||||
} MD_BLOCKTYPE;
|
||||
|
||||
/* Span represents an in-line piece of a document which should be rendered with
|
||||
* the same font, color and other attributes. A sequence of spans forms a block
|
||||
* like paragraph or list item. */
|
||||
typedef enum MD_SPANTYPE {
|
||||
/* <em>...</em> */
|
||||
MD_SPAN_EM,
|
||||
|
||||
/* <strong>...</strong> */
|
||||
MD_SPAN_STRONG,
|
||||
|
||||
/* <a href="xxx">...</a>
|
||||
* Detail: Structure MD_SPAN_A_DETAIL. */
|
||||
MD_SPAN_A,
|
||||
|
||||
/* <img src="xxx">...</a>
|
||||
* Detail: Structure MD_SPAN_IMG_DETAIL.
|
||||
* Note: Image text can contain nested spans and even nested images.
|
||||
* If rendered into ALT attribute of HTML <IMG> tag, it's responsibility
|
||||
* of the parser to deal with it.
|
||||
*/
|
||||
MD_SPAN_IMG,
|
||||
|
||||
/* <code>...</code> */
|
||||
MD_SPAN_CODE,
|
||||
|
||||
/* <del>...</del>
|
||||
* Note: Recognized only when MD_FLAG_STRIKETHROUGH is enabled.
|
||||
*/
|
||||
MD_SPAN_DEL,
|
||||
|
||||
/* For recognizing inline ($) and display ($$) equations
|
||||
* Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled.
|
||||
*/
|
||||
MD_SPAN_LATEXMATH,
|
||||
MD_SPAN_LATEXMATH_DISPLAY,
|
||||
|
||||
/* Wiki links
|
||||
* Note: Recognized only when MD_FLAG_WIKILINKS is enabled.
|
||||
*/
|
||||
MD_SPAN_WIKILINK,
|
||||
|
||||
/* <u>...</u>
|
||||
* Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */
|
||||
MD_SPAN_U
|
||||
} MD_SPANTYPE;
|
||||
|
||||
/* Text is the actual textual contents of span. */
|
||||
typedef enum MD_TEXTTYPE {
|
||||
/* Normal text. */
|
||||
MD_TEXT_NORMAL = 0,
|
||||
|
||||
/* NULL character. CommonMark requires replacing NULL character with
|
||||
* the replacement char U+FFFD, so this allows caller to do that easily. */
|
||||
MD_TEXT_NULLCHAR,
|
||||
|
||||
/* Line breaks.
|
||||
* Note these are not sent from blocks with verbatim output (MD_BLOCK_CODE
|
||||
* or MD_BLOCK_HTML). In such cases, '\n' is part of the text itself. */
|
||||
MD_TEXT_BR, /* <br> (hard break) */
|
||||
MD_TEXT_SOFTBR, /* '\n' in source text where it is not semantically meaningful (soft break) */
|
||||
|
||||
/* Entity.
|
||||
* (a) Named entity, e.g.
|
||||
* (Note MD4C does not have a list of known entities.
|
||||
* Anything matching the regexp /&[A-Za-z][A-Za-z0-9]{1,47};/ is
|
||||
* treated as a named entity.)
|
||||
* (b) Numerical entity, e.g. Ӓ
|
||||
* (c) Hexadecimal entity, e.g. ካ
|
||||
*
|
||||
* As MD4C is mostly encoding agnostic, application gets the verbatim
|
||||
* entity text into the MD_PARSER::text_callback(). */
|
||||
MD_TEXT_ENTITY,
|
||||
|
||||
/* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`).
|
||||
* If it is inside MD_BLOCK_CODE, it includes spaces for indentation and
|
||||
* '\n' for new lines. MD_TEXT_BR and MD_TEXT_SOFTBR are not sent for this
|
||||
* kind of text. */
|
||||
MD_TEXT_CODE,
|
||||
|
||||
/* Text is a raw HTML. If it is contents of a raw HTML block (i.e. not
|
||||
* an inline raw HTML), then MD_TEXT_BR and MD_TEXT_SOFTBR are not used.
|
||||
* The text contains verbatim '\n' for the new lines. */
|
||||
MD_TEXT_HTML,
|
||||
|
||||
/* Text is inside an equation. This is processed the same way as inlined code
|
||||
* spans (`code`). */
|
||||
MD_TEXT_LATEXMATH
|
||||
} MD_TEXTTYPE;
|
||||
|
||||
|
||||
/* Alignment enumeration. */
|
||||
typedef enum MD_ALIGN {
|
||||
MD_ALIGN_DEFAULT = 0, /* When unspecified. */
|
||||
MD_ALIGN_LEFT,
|
||||
MD_ALIGN_CENTER,
|
||||
MD_ALIGN_RIGHT
|
||||
} MD_ALIGN;
|
||||
|
||||
|
||||
/* String attribute.
|
||||
*
|
||||
* This wraps strings which are outside of a normal text flow and which are
|
||||
* propagated within various detailed structures, but which still may contain
|
||||
* string portions of different types like e.g. entities.
|
||||
*
|
||||
* So, for example, lets consider this image:
|
||||
*
|
||||
* 
|
||||
*
|
||||
* The image alt text is propagated as a normal text via the MD_PARSER::text()
|
||||
* callback. However, the image title ('foo " bar') is propagated as
|
||||
* MD_ATTRIBUTE in MD_SPAN_IMG_DETAIL::title.
|
||||
*
|
||||
* Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following:
|
||||
* -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0)
|
||||
* -- [1]: """ (substr_types[1] == MD_TEXT_ENTITY; substr_offsets[1] == 4)
|
||||
* -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10)
|
||||
* -- [3]: (n/a) (n/a ; substr_offsets[3] == 14)
|
||||
*
|
||||
* Note that these invariants are always guaranteed:
|
||||
* -- substr_offsets[0] == 0
|
||||
* -- substr_offsets[LAST+1] == size
|
||||
* -- Currently, only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR
|
||||
* substrings can appear. This could change only of the specification
|
||||
* changes.
|
||||
*/
|
||||
typedef struct MD_ATTRIBUTE {
|
||||
const MD_CHAR* text;
|
||||
MD_SIZE size;
|
||||
const MD_TEXTTYPE* substr_types;
|
||||
const MD_OFFSET* substr_offsets;
|
||||
} MD_ATTRIBUTE;
|
||||
|
||||
|
||||
/* Detailed info for MD_BLOCK_UL. */
|
||||
typedef struct MD_BLOCK_UL_DETAIL {
|
||||
int is_tight; /* Non-zero if tight list, zero if loose. */
|
||||
MD_CHAR mark; /* Item bullet character in MarkDown source of the list, e.g. '-', '+', '*'. */
|
||||
} MD_BLOCK_UL_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_OL. */
|
||||
typedef struct MD_BLOCK_OL_DETAIL {
|
||||
unsigned start; /* Start index of the ordered list. */
|
||||
int is_tight; /* Non-zero if tight list, zero if loose. */
|
||||
MD_CHAR mark_delimiter; /* Character delimiting the item marks in MarkDown source, e.g. '.' or ')' */
|
||||
} MD_BLOCK_OL_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_LI. */
|
||||
typedef struct MD_BLOCK_LI_DETAIL {
|
||||
int is_task; /* Can be non-zero only with MD_FLAG_TASKLISTS */
|
||||
MD_CHAR task_mark; /* If is_task, then one of 'x', 'X' or ' '. Undefined otherwise. */
|
||||
MD_OFFSET task_mark_offset; /* If is_task, then offset in the input of the char between '[' and ']'. */
|
||||
} MD_BLOCK_LI_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_H. */
|
||||
typedef struct MD_BLOCK_H_DETAIL {
|
||||
unsigned level; /* Header level (1 - 6) */
|
||||
} MD_BLOCK_H_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_CODE. */
|
||||
typedef struct MD_BLOCK_CODE_DETAIL {
|
||||
MD_ATTRIBUTE info;
|
||||
MD_ATTRIBUTE lang;
|
||||
MD_CHAR fence_char; /* The character used for fenced code block; or zero for indented code block. */
|
||||
} MD_BLOCK_CODE_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_TABLE. */
|
||||
typedef struct MD_BLOCK_TABLE_DETAIL {
|
||||
unsigned col_count; /* Count of columns in the table. */
|
||||
unsigned head_row_count; /* Count of rows in the table header (currently always 1) */
|
||||
unsigned body_row_count; /* Count of rows in the table body */
|
||||
} MD_BLOCK_TABLE_DETAIL;
|
||||
|
||||
/* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */
|
||||
typedef struct MD_BLOCK_TD_DETAIL {
|
||||
MD_ALIGN align;
|
||||
} MD_BLOCK_TD_DETAIL;
|
||||
|
||||
/* Detailed info for MD_SPAN_A. */
|
||||
typedef struct MD_SPAN_A_DETAIL {
|
||||
MD_ATTRIBUTE href;
|
||||
MD_ATTRIBUTE title;
|
||||
int is_autolink; /* nonzero if this is an autolink */
|
||||
} MD_SPAN_A_DETAIL;
|
||||
|
||||
/* Detailed info for MD_SPAN_IMG. */
|
||||
typedef struct MD_SPAN_IMG_DETAIL {
|
||||
MD_ATTRIBUTE src;
|
||||
MD_ATTRIBUTE title;
|
||||
} MD_SPAN_IMG_DETAIL;
|
||||
|
||||
/* Detailed info for MD_SPAN_WIKILINK. */
|
||||
typedef struct MD_SPAN_WIKILINK {
|
||||
MD_ATTRIBUTE target;
|
||||
} MD_SPAN_WIKILINK_DETAIL;
|
||||
|
||||
/* Flags specifying extensions/deviations from CommonMark specification.
|
||||
*
|
||||
* By default (when MD_PARSER::flags == 0), we follow CommonMark specification.
|
||||
* The following flags may allow some extensions or deviations from it.
|
||||
*/
|
||||
#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */
|
||||
#define MD_FLAG_PERMISSIVEATXHEADERS 0x0002 /* Do not require space in ATX headers ( ###header ) */
|
||||
#define MD_FLAG_PERMISSIVEURLAUTOLINKS 0x0004 /* Recognize URLs as autolinks even without '<', '>' */
|
||||
#define MD_FLAG_PERMISSIVEEMAILAUTOLINKS 0x0008 /* Recognize e-mails as autolinks even without '<', '>' and 'mailto:' */
|
||||
#define MD_FLAG_NOINDENTEDCODEBLOCKS 0x0010 /* Disable indented code blocks. (Only fenced code works.) */
|
||||
#define MD_FLAG_NOHTMLBLOCKS 0x0020 /* Disable raw HTML blocks. */
|
||||
#define MD_FLAG_NOHTMLSPANS 0x0040 /* Disable raw HTML (inline). */
|
||||
#define MD_FLAG_TABLES 0x0100 /* Enable tables extension. */
|
||||
#define MD_FLAG_STRIKETHROUGH 0x0200 /* Enable strikethrough extension. */
|
||||
#define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */
|
||||
#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */
|
||||
#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */
|
||||
#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */
|
||||
#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */
|
||||
#define MD_FLAG_HARD_SOFT_BREAKS 0x8000 /* Force all soft breaks to act as hard breaks. */
|
||||
|
||||
#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS)
|
||||
#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS)
|
||||
|
||||
/* Convenient sets of flags corresponding to well-known Markdown dialects.
|
||||
*
|
||||
* Note we may only support subset of features of the referred dialect.
|
||||
* The constant just enables those extensions which bring us as close as
|
||||
* possible given what features we implement.
|
||||
*
|
||||
* ABI compatibility note: Meaning of these can change in time as new
|
||||
* extensions, bringing the dialect closer to the original, are implemented.
|
||||
*/
|
||||
#define MD_DIALECT_COMMONMARK 0
|
||||
#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS)
|
||||
|
||||
/* Parser structure.
|
||||
*/
|
||||
typedef struct MD_PARSER {
|
||||
/* Reserved. Set to zero.
|
||||
*/
|
||||
unsigned abi_version;
|
||||
|
||||
/* Dialect options. Bitmask of MD_FLAG_xxxx values.
|
||||
*/
|
||||
unsigned flags;
|
||||
|
||||
/* Caller-provided rendering callbacks.
|
||||
*
|
||||
* For some block/span types, more detailed information is provided in a
|
||||
* type-specific structure pointed by the argument 'detail'.
|
||||
*
|
||||
* The last argument of all callbacks, 'userdata', is just propagated from
|
||||
* md_parse() and is available for any use by the application.
|
||||
*
|
||||
* Note any strings provided to the callbacks as their arguments or as
|
||||
* members of any detail structure are generally not zero-terminated.
|
||||
* Application has to take the respective size information into account.
|
||||
*
|
||||
* Any rendering callback may abort further parsing of the document by
|
||||
* returning non-zero.
|
||||
*/
|
||||
int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
|
||||
int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
|
||||
|
||||
int (*enter_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
|
||||
int (*leave_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
|
||||
|
||||
int (*text)(MD_TEXTTYPE /*type*/, const MD_CHAR* /*text*/, MD_SIZE /*size*/, void* /*userdata*/);
|
||||
|
||||
/* Debug callback. Optional (may be NULL).
|
||||
*
|
||||
* If provided and something goes wrong, this function gets called.
|
||||
* This is intended for debugging and problem diagnosis for developers;
|
||||
* it is not intended to provide any errors suitable for displaying to an
|
||||
* end user.
|
||||
*/
|
||||
void (*debug_log)(const char* /*msg*/, void* /*userdata*/);
|
||||
|
||||
/* Reserved. Set to NULL.
|
||||
*/
|
||||
void (*syntax)(void);
|
||||
} MD_PARSER;
|
||||
|
||||
|
||||
/* For backward compatibility. Do not use in new code.
|
||||
*/
|
||||
typedef MD_PARSER MD_RENDERER;
|
||||
|
||||
|
||||
/* Parse the Markdown document stored in the string 'text' of size 'size'.
|
||||
* The parser provides callbacks to be called during the parsing so the
|
||||
* caller can render the document on the screen or convert the Markdown
|
||||
* to another format.
|
||||
*
|
||||
* Zero is returned on success. If a runtime error occurs (e.g. a memory
|
||||
* fails), -1 is returned. If the processing is aborted due any callback
|
||||
* returning non-zero, the return value of the callback is returned.
|
||||
*/
|
||||
int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" { */
|
||||
#endif
|
||||
|
||||
#endif /* MD4C_H */
|
||||
Reference in New Issue
Block a user