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 @@
line 1 line 2
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, "- 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,
},