From 2938af6f0bb4bd555430af7a175b8626fbab6c93 Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Wed, 30 Jun 2021 19:13:07 -0700 Subject: [PATCH] adds onCodeBlock callback re #11 --- example/example.html | 14 ++-- example/example.js | 15 +++- markdown.d.ts | 21 ++++++ src/common.h | 9 ++- src/fmt_html.c | 172 ++++++++++++++++++++++++++----------------- src/fmt_html.h | 18 ++++- src/md.c | 23 +++--- src/md.js | 76 +++++++++++++++++-- src/wbuf.c | 2 +- src/wbuf.h | 1 - wasmc.js | 4 + 11 files changed, 262 insertions(+), 93 deletions(-) diff --git a/example/example.html b/example/example.html index eeeea9a..e46b151 100644 --- a/example/example.html +++ b/example/example.html @@ -13,16 +13,16 @@

Anothe

line 1 line 2

Code & Poetry

-
You can also indent
-blocks to display
-code or poetry.
+
YOU CAN ALSO INDENT
+BLOCKS TO DISPLAY
+CODE OR POETRY.
 
-Indented code/poetry blocks
-can be hard-wrapped.
+INDENTED CODE/POETRY BLOCKS
+CAN BE HARD-WRAPPED.
 

Or, wrap your code in three backticks:

-
function codeBlocks() {
-  return "Can be inserted"
+
FUNCTION CODEBLOCKS() {
+  RETURN "CAN BE INSERTED"
 }
 

Block Quotes

diff --git a/example/example.js b/example/example.js index bbd795e..4b41b6e 100644 --- a/example/example.js +++ b/example/example.js @@ -1,8 +1,15 @@ const fs = require("fs") const md = require("../dist/markdown.node.js") +// const md = require("../build/debug/markdown.node.js") const source = fs.readFileSync(__dirname + "/example.md") -const outbuf = md.parse(source, { bytes: true }) +const outbuf = md.parse(source, { + bytes: true, + onCodeBlock(lang, body) { + console.log(`onCodeBlock (${lang})`) + return html_escape(body.toString().toUpperCase()) + }, +}) const outfile = __dirname + "/example.html" console.log("write", outfile) fs.writeFileSync(outfile, outbuf) @@ -24,3 +31,9 @@ if (process.argv.includes("-bench")) { const timeSpent = Date.now() - timeStart console.log(`benchmark end -- avg parse time: ${((timeSpent / ntotal) * 1000).toFixed(1)}us`) } + +function html_escape(str) { + return str.replace(/[&<>'"]/g, tag => ({ + '&': '&','<': '<','>': '>',"'": ''','"': '"' + }[tag])) +} diff --git a/markdown.d.ts b/markdown.d.ts index 30c43ae..6e7d1a5 100644 --- a/markdown.d.ts +++ b/markdown.d.ts @@ -28,10 +28,31 @@ export interface ParseOptions { */ bytes? :boolean + /** + * onCodeBlock is an optional callback which if provided is called for each code block. + * langname holds the "language tag", if any, of the block. + * + * The returned value is inserted into the resulting HTML verbatim, without HTML escaping. + * Thus, you should take care of properly escaping any special HTML characters. + * + * If the function returns null or undefined, or an exception occurs, the body will be + * included as-is after going through HTML escaping. + * + * Note that use of this callback has an adverse impact on performance as it casues + * calls and data to be bridged between WASM and JS on every invocation. + */ + onCodeBlock? :(langname :string, body :UTF8Bytes) => Uint8Array|string|null|undefined + /** @depreceated use "bytes" instead (v1.1.1) */ asMemoryView? :boolean } +/** UTF8Bytes is a Uint8Array representing UTF8 text */ +export interface UTF8Bytes extends Uint8Array { + /** toString returns a UTF8 decoded string (lazily decoded and cached) */ + toString() :string +} + /** Flags that customize Markdown parsing */ export enum ParseFlags { /** In TEXT, collapse non-trivial whitespace into single ' ' */ COLLAPSE_WHITESPACE, diff --git a/src/common.h b/src/common.h index 956a552..836b2a5 100644 --- a/src/common.h +++ b/src/common.h @@ -54,7 +54,14 @@ typedef int32_t i32; #endif #if DEBUG #include - #define dlog(...) printf(__VA_ARGS__) + #define dlog(fmt, ...) printf(fmt "\n", ##__VA_ARGS__) #else #define dlog(...) #endif /* DEBUG > 0 */ + +#include "wbuf.h" + +typedef int(*JSTextFilterFun)( + const char* metaptr, u32 metalen, + const char* inptr, u32 inlen, + const char** outptrp); // return outlen diff --git a/src/fmt_html.c b/src/fmt_html.c index a2788f8..2e71b04 100644 --- a/src/fmt_html.c +++ b/src/fmt_html.c @@ -30,12 +30,12 @@ #include "fmt_html.h" #include "md4c.h" -typedef struct HtmlRenderer_st { - WBuf* outbuf; - int imgnest; - int addanchor; - u32 flags; -} HtmlRenderer; +// typedef struct FmtHTML_st { +// WBuf* outbuf; +// int imgnest; +// int addanchor; +// u32 flags; +// } FmtHTML; static char htmlEscapeMap[256] = { @@ -61,20 +61,20 @@ static char htmlEscapeMap[256] = { static const char ucReplacementUTF8[] = { 0xef, 0xbf, 0xbd }; -static inline void render_text(HtmlRenderer* r, const char* pch, size_t len) { +static inline void render_text(FmtHTML* r, const char* pch, size_t len) { WBufAppendBytes(r->outbuf, pch, len); } -static inline void render_literal(HtmlRenderer* r, const char* cs) { +static inline void render_literal(FmtHTML* r, const char* cs) { WBufAppendBytes(r->outbuf, cs, strlen(cs)); } -static inline void render_char(HtmlRenderer* r, char c) { +static inline void render_char(FmtHTML* r, char c) { WBufAppendc(r->outbuf, c); } -static void render_html_escaped(HtmlRenderer* r, const char* data, size_t size) { +static void render_html_escaped(FmtHTML* r, const char* data, size_t size) { MD_OFFSET beg = 0; MD_OFFSET off = 0; @@ -167,7 +167,7 @@ static size_t WBufAppendSlug(WBuf* b, const char* pch, size_t len) { } -static void render_attribute(HtmlRenderer* r, const MD_ATTRIBUTE* attr) { +static void render_attribute(FmtHTML* r, const MD_ATTRIBUTE* attr) { int i; for (i = 0; attr->substr_offsets[i] < attr->size; i++) { MD_TEXTTYPE type = attr->substr_types[i]; @@ -183,7 +183,7 @@ static void render_attribute(HtmlRenderer* r, const MD_ATTRIBUTE* attr) { } -static void render_open_ol_block(HtmlRenderer* r, const MD_BLOCK_OL_DETAIL* det) { +static void render_open_ol_block(FmtHTML* r, const MD_BLOCK_OL_DETAIL* det) { if (det->start == 1) { render_literal(r, "
    \n"); } else { @@ -193,7 +193,7 @@ static void render_open_ol_block(HtmlRenderer* r, const MD_BLOCK_OL_DETAIL* det) } } -static void render_open_li_block(HtmlRenderer* r, const MD_BLOCK_LI_DETAIL* det) { +static void render_open_li_block(FmtHTML* r, const MD_BLOCK_LI_DETAIL* det) { if (det->is_task) { render_literal(r, "
  1. task_mark == 'x' || det->task_mark == 'X') { @@ -205,7 +205,7 @@ static void render_open_li_block(HtmlRenderer* r, const MD_BLOCK_LI_DETAIL* det) } } -static void render_open_code_block(HtmlRenderer* r, const MD_BLOCK_CODE_DETAIL* det) { +static void render_open_code_block(FmtHTML* r, const MD_BLOCK_CODE_DETAIL* det) { render_literal(r, "
    lang.text != NULL) {
         render_literal(r, " class=\"language-");
    @@ -213,9 +213,41 @@ static void render_open_code_block(HtmlRenderer* r, const MD_BLOCK_CODE_DETAIL*
         render_char(r, '"');
       }
       render_char(r, '>');
    +  r->codeBlockNest++;
     }
     
    -static void render_open_td_block(HtmlRenderer* r, bool isTH, const MD_BLOCK_TD_DETAIL* det) {
    +static void render_close_code_block(FmtHTML* r, const MD_BLOCK_CODE_DETAIL* det) {
    +  dlog("end code block (lang \"%.*s\")", (int)det->lang.size, det->lang.text);
    +
    +  r->codeBlockNest--;
    +
    +  if (r->onCodeBlock) {
    +    const char* text = r->tmpbuf.start;
    +    size_t len = WBufLen(&r->tmpbuf);
    +
    +    int outlen = -1;
    +
    +    if (len <= 0x7FFFFFFF) {
    +      const char* outptr = NULL;
    +      outlen = r->onCodeBlock(det->lang.text, (u32)det->lang.size, text, (u32)len, &outptr);
    +      if (outlen > 0 && outptr != NULL)
    +        WBufAppendBytes(r->outbuf, outptr, (size_t)outlen);
    +      if (outptr != NULL)
    +        free((void*)outptr);
    +    }
    +
    +    if (outlen < 0) {
    +      // The function failed or opted out of taking care of formatting
    +      render_html_escaped(r, text, len);
    +    }
    +
    +    WBufReset(&r->tmpbuf);
    +  }
    +
    +  render_literal(r, "
    \n"); +} + +static void render_open_td_block(FmtHTML* r, bool isTH, const MD_BLOCK_TD_DETAIL* det) { render_text(r, isTH ? "align) { case MD_ALIGN_LEFT: render_literal(r, " align=\"left\">"); break; @@ -225,7 +257,7 @@ static void render_open_td_block(HtmlRenderer* r, bool isTH, const MD_BLOCK_TD_D } } -static void render_open_a_span(HtmlRenderer* r, const MD_SPAN_A_DETAIL* det) { +static void render_open_a_span(FmtHTML* r, const MD_SPAN_A_DETAIL* det) { render_literal(r, "href); if (det->title.text != NULL) { @@ -235,14 +267,14 @@ static void render_open_a_span(HtmlRenderer* r, const MD_SPAN_A_DETAIL* det) { render_literal(r, "\">"); } -static void render_open_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det) { +static void render_open_img_span(FmtHTML* r, const MD_SPAN_IMG_DETAIL* det) { render_literal(r, "src); render_literal(r, "\" alt=\""); r->imgnest++; } -static void render_close_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det) { +static void render_close_img_span(FmtHTML* r, const MD_SPAN_IMG_DETAIL* det) { if(det->title.text != NULL) { render_literal(r, "\" title=\""); render_attribute(r, &det->title); @@ -251,7 +283,7 @@ static void render_close_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det r->imgnest--; } -static void render_open_wikilink_span(HtmlRenderer* r, const MD_SPAN_WIKILINK_DETAIL* det) { +static void render_open_wikilink_span(FmtHTML* r, const MD_SPAN_WIKILINK_DETAIL* det) { render_literal(r, "target); render_literal(r, "\">"); @@ -266,30 +298,30 @@ static void render_open_wikilink_span(HtmlRenderer* r, const MD_SPAN_WIKILINK_DE static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) { static const MD_CHAR* head[6] = { "

    ", "

    ", "

    ", "

    ", "

    ", "
    " }; - HtmlRenderer* r = (HtmlRenderer*) userdata; + FmtHTML* r = (FmtHTML*) userdata; switch(type) { - case MD_BLOCK_DOC: /* noop */ break; - case MD_BLOCK_QUOTE: render_literal(r, "
    \n"); break; - case MD_BLOCK_UL: render_literal(r, "
      \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_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "
      \n" : "
      \n"); break; + case MD_BLOCK_DOC: /* noop */ break; + case MD_BLOCK_QUOTE: render_literal(r, "
      \n"); break; + case MD_BLOCK_UL: render_literal(r, "
        \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_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "
        \n" : "
        \n"); break; case MD_BLOCK_H: { render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); r->addanchor = 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_literal(r, "

        "); break; - case MD_BLOCK_TABLE: render_literal(r, "\n"); break; - case MD_BLOCK_THEAD: render_literal(r, "\n"); break; - case MD_BLOCK_TBODY: render_literal(r, "\n"); break; - case MD_BLOCK_TR: render_literal(r, "\n"); break; - case MD_BLOCK_TH: render_open_td_block(r, true, (MD_BLOCK_TD_DETAIL*)detail); break; - case MD_BLOCK_TD: render_open_td_block(r, false, (MD_BLOCK_TD_DETAIL*)detail); 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_literal(r, "

        "); break; + case MD_BLOCK_TABLE: render_literal(r, "

        \n"); break; + case MD_BLOCK_THEAD: render_literal(r, "\n"); break; + case MD_BLOCK_TBODY: render_literal(r, "\n"); break; + case MD_BLOCK_TR: render_literal(r, "\n"); break; + case MD_BLOCK_TH: render_open_td_block(r, true, (MD_BLOCK_TD_DETAIL*)detail); break; + case MD_BLOCK_TD: render_open_td_block(r, false, (MD_BLOCK_TD_DETAIL*)detail); break; } return 0; @@ -297,32 +329,32 @@ static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) static int leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) { static const MD_CHAR* head[6] = { "\n", "\n", "\n", "\n", "\n", "\n" }; - HtmlRenderer* r = (HtmlRenderer*) userdata; + FmtHTML* r = (FmtHTML*) userdata; switch(type) { - case MD_BLOCK_DOC: /*noop*/ break; - case MD_BLOCK_QUOTE: render_literal(r, "\n"); break; - case MD_BLOCK_UL: render_literal(r, "\n"); break; - case MD_BLOCK_OL: render_literal(r, "\n"); break; - case MD_BLOCK_LI: render_literal(r, "\n"); break; - case MD_BLOCK_HR: /*noop*/ break; - case MD_BLOCK_H: render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break; - case MD_BLOCK_CODE: render_literal(r, "\n"); break; - case MD_BLOCK_HTML: /* noop */ break; - case MD_BLOCK_P: render_literal(r, "

        \n"); break; - case MD_BLOCK_TABLE: render_literal(r, "
        \n"); break; - case MD_BLOCK_THEAD: render_literal(r, "\n"); break; - case MD_BLOCK_TBODY: render_literal(r, "\n"); break; - case MD_BLOCK_TR: render_literal(r, "\n"); break; - case MD_BLOCK_TH: render_literal(r, "\n"); break; - case MD_BLOCK_TD: render_literal(r, "\n"); break; + case MD_BLOCK_DOC: /*noop*/ break; + case MD_BLOCK_QUOTE: render_literal(r, "

      \n"); break; + case MD_BLOCK_UL: render_literal(r, "
    \n"); break; + case MD_BLOCK_OL: render_literal(r, "
\n"); break; + case MD_BLOCK_LI: render_literal(r, "\n"); break; + case MD_BLOCK_HR: /*noop*/ break; + case MD_BLOCK_H: render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break; + case MD_BLOCK_CODE: render_close_code_block(r, (const MD_BLOCK_CODE_DETAIL*)detail); break; + case MD_BLOCK_HTML: /* noop */ break; + case MD_BLOCK_P: render_literal(r, "

\n"); break; + case MD_BLOCK_TABLE: render_literal(r, "\n"); break; + case MD_BLOCK_THEAD: render_literal(r, "\n"); break; + case MD_BLOCK_TBODY: render_literal(r, "\n"); break; + case MD_BLOCK_TR: render_literal(r, "\n"); break; + case MD_BLOCK_TH: render_literal(r, "\n"); break; + case MD_BLOCK_TD: render_literal(r, "\n"); break; } return 0; } static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { - HtmlRenderer* r = (HtmlRenderer*) userdata; + FmtHTML* r = (FmtHTML*) userdata; if(r->imgnest > 0) { /* We are inside a Markdown image label. Markdown allows to use any @@ -360,7 +392,7 @@ static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { } static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { - HtmlRenderer* r = (HtmlRenderer*) userdata; + FmtHTML* r = (FmtHTML*) userdata; if(r->imgnest > 0) { /* Ditto as in enter_span_callback(), except we have to allow the @@ -387,7 +419,12 @@ static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) { } static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) { - HtmlRenderer* r = (HtmlRenderer*) userdata; + FmtHTML* r = (FmtHTML*) userdata; + + if (r->codeBlockNest && r->onCodeBlock) { + WBufAppendBytes(&r->tmpbuf, text, size); + return 0; + } if (r->addanchor) { r->addanchor = 0; @@ -409,7 +446,7 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo } } - switch(type) { + switch (type) { case MD_TEXT_NULLCHAR: render_text(r, ucReplacementUTF8, sizeof(ucReplacementUTF8)); break; case MD_TEXT_BR: render_literal( @@ -435,18 +472,15 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo // dlog("MD4C: %s\n", msg); // } -int fmt_html( - const MD_CHAR* input, - MD_SIZE input_size, - WBuf* outbuf, - u32 parser_flags, - u32 render_flags -) { - HtmlRenderer render = { outbuf, 0, 0, render_flags }; +int fmt_html(const MD_CHAR* input, MD_SIZE input_size, FmtHTML* fmt) { + fmt->imgnest = 0; + fmt->addanchor = 0; + fmt->codeBlockNest = 0; + fmt->tmpbuf = (WBuf){0}; MD_PARSER parser = { 0, - parser_flags, + fmt->parserFlags, enter_block_callback, leave_block_callback, enter_span_callback, @@ -456,5 +490,11 @@ int fmt_html( NULL }; - return md_parse(input, input_size, &parser, (void*) &render); + WBufInit(&fmt->tmpbuf); + + int res = md_parse(input, input_size, &parser, (void*)fmt); + + WBufFree(&fmt->tmpbuf); + + return res; } diff --git a/src/fmt_html.h b/src/fmt_html.h index ed38124..a903981 100644 --- a/src/fmt_html.h +++ b/src/fmt_html.h @@ -1,6 +1,20 @@ #pragma once -#include "wbuf.h" #define MD_HTML_FLAG_XHTML 0x0008 // instead of e.g.
, generate
-int fmt_html(const char* input, u32 inputlen, WBuf* outbuf, u32 parserFlags, u32 renderFlags); +typedef struct FmtHTML { + u32 flags; // MD_HTML_FLAG_* + u32 parserFlags; // passed along to md_parse + WBuf* outbuf; + + // optional callbacks + JSTextFilterFun onCodeBlock; + + // internal state + int imgnest; + int addanchor; + int codeBlockNest; + WBuf tmpbuf; +} FmtHTML; + +int fmt_html(const char* input, u32 inputlen, FmtHTML* fmt); diff --git a/src/md.c b/src/md.c index 1ad2ebf..b0c6225 100644 --- a/src/md.c +++ b/src/md.c @@ -1,7 +1,6 @@ #include #include "common.h" #include "wlib.h" -#include "wbuf.h" #include "fmt_html.h" // #include "fmt_json.h" @@ -20,7 +19,7 @@ typedef enum ErrorCode { #if DEBUG void __attribute__((constructor)) init() { - dlog("WASM INIT\n"); + dlog("WASM INIT"); } #endif @@ -35,21 +34,27 @@ export size_t parseUTF8( u32 inbuflen, u32 parser_flags, OutputFlags outflags, - const char** outptr + const char** outptr, + JSTextFilterFun onCodeBlock ) { - dlog("parseUTF8 called with inbufptr=%p inbuflen=%u\n", inbufptr, inbuflen); + dlog("parseUTF8 called with inbufptr=%p inbuflen=%u", inbufptr, inbuflen); WBufReset(&outbuf); if (outflags & OutputFlagHTML) { WBufReserve(&outbuf, inbuflen * 2); // approximate output size to minimize reallocations - u32 render_flags = 0; - if (outflags & OutputFlagXHTML) { - render_flags |= MD_HTML_FLAG_XHTML; - } + FmtHTML fmt = { + .flags = 0, + .parserFlags = parser_flags, + .outbuf = &outbuf, + .onCodeBlock = onCodeBlock, + }; + + if (outflags & OutputFlagXHTML) + fmt.flags |= MD_HTML_FLAG_XHTML; - if (fmt_html(inbufptr, inbuflen, &outbuf, parser_flags, render_flags) != 0) { + if (fmt_html(inbufptr, inbuflen, &fmt) != 0) { // fmt_html returns status of md_parse which only fails in extreme cases // like when out of memory. md4c does not provide error codes or error messages. WErrSet(ERR_MD_PARSE, "md parser error"); diff --git a/src/md.js b/src/md.js index 20d93ce..c5d05cd 100644 --- a/src/md.js +++ b/src/md.js @@ -1,4 +1,10 @@ -import { utf8, withTmpBytePtr, withOutPtr, werrCheck } from "./wlib" +import { + utf8, + withTmpBytePtr, + withOutPtr, + werrCheck, + mallocbuf, +} from "./wlib" export const ready = Module.ready @@ -41,6 +47,7 @@ const OutputFlags = { XHTML: 1 << 1, // Output XHTML (only has effect with HTML flag set) } + export function parse(source, options) { options = options || {} @@ -66,11 +73,16 @@ export function parse(source, options) { throw new Error(`invalid format "${options.format}"`) } - let buf = typeof source == "string" ? utf8.encode(source) : source + let onCodeBlockPtr = options.onCodeBlock ? create_onCodeBlock_fn(options.onCodeBlock) : 0 + + let buf = as_byte_array(source) let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) => - _parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr) + _parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr, onCodeBlockPtr) )) + if (options.onCodeBlock) + removeFunction(onCodeBlockPtr) + // check for error and throw if needed werrCheck() @@ -79,8 +91,62 @@ export function parse(source, options) { // console.log(utf8.decode(outbuf)) // } - if (options.bytes || options.asMemoryView) { + if (options.bytes || options.asMemoryView) return outbuf - } + return utf8.decode(outbuf) } + + +function create_onCodeBlock_fn(onCodeBlock) { + // See https://emscripten.org/docs/porting/connecting_cpp_and_javascript/ + // Interacting-with-code.html#calling-javascript-functions-as-function-pointers-from-c + // + // Function's C type: JSTextFilterFun + // (metaptr ptr, metalen ptr, inptr ptr, inlen ptr, outptr ptr) -> outlen int + const fnptr = addFunction(function(metaptr, metalen, inptr, inlen, outptr) { + try { + // lang is the "language" tag, if any, provided with the code block + const lang = metalen > 0 ? utf8.decode(HEAPU8.subarray(metaptr, metaptr + metalen)) : "" + + // body is a view into heap memory of the segment of source (UTF8 bytes) + const body = HEAPU8.subarray(inptr, inptr + inlen) + let bodystr = undefined + body.toString = () => (bodystr || (bodystr = utf8.decode(body))) + + // result is the result from the onCodeBlock function + let result = null + result = onCodeBlock(lang, body) + + if (result === null || result === undefined) { + // Callback indicates that it does not wish to filter. + // The md.c implementation will html-encode the body. + return -1 + } + + let resbuf = as_byte_array(result) + if (resbuf.length > 0) { + // copy resbuf to WASM heap memory + const resptr = mallocbuf(resbuf, resbuf.length) + // write pointer value + HEAPU32[outptr >> 2 /* == outptr / 4 */] = resptr + // Note: fmt_html.c calls free(resptr) + } + + return resbuf.length + } catch (err) { + console.error(`error in markdown onCodeBlock callback: ${err.stack||err}`) + return -1 + } + }, "iiiiii") + return fnptr +} + + +function as_byte_array(something) { + if (typeof something == "string") + return utf8.encode(something) + if (something instanceof Uint8Array) + return something + return new Uint8Array(something) +} diff --git a/src/wbuf.c b/src/wbuf.c index 121c594..ab5f438 100644 --- a/src/wbuf.c +++ b/src/wbuf.c @@ -1,4 +1,4 @@ -#include "wbuf.h" +#include "common.h" void WBufInit(WBuf* b) { b->start = 0; diff --git a/src/wbuf.h b/src/wbuf.h index 4f0df26..343a774 100644 --- a/src/wbuf.h +++ b/src/wbuf.h @@ -1,5 +1,4 @@ #pragma once -#include "common.h" typedef struct WBuf_s { char* start; // pointer to start of data diff --git a/wasmc.js b/wasmc.js index c92392b..ab14731 100644 --- a/wasmc.js +++ b/wasmc.js @@ -23,6 +23,10 @@ const m = { ] : [ // release flags ]), + lflags: [ + // force inclusion of addFunction & removeFunction in release builds (emcc bug?) + "-s","EXPORTED_RUNTIME_METHODS=addFunction,removeFunction", + ], constants: { VERSION: package.version, },