From d0e16e0491e76d361e3b92c1b5ae9b8f6d84e9d9 Mon Sep 17 00:00:00 2001 From: Graham Nelson Date: Wed, 6 Sep 2023 23:38:07 +0100 Subject: [PATCH] Improvements to IFM --- docs/foundation-module/4-chr.html | 2 +- docs/foundation-module/4-pm.html | 6 +- docs/foundation-module/4-sm.html | 26 +- docs/foundation-module/5-htm.html | 14 +- docs/foundation-module/5-im.html | 229 +++++++++++------- docs/foundation-module/5-mpi2.html | 1 + docs/foundation-module/5-mr.html | 31 ++- docs/foundation-module/5-mrk.html | 141 +++++------ docs/foundation-module/9-pl.html | 2 +- docs/foundation-module/9-tp.html | 4 +- docs/goldbach/goldbach.pdf | Bin 251998 -> 251998 bytes .../Chapter 5/Inform-Flavoured Markdown.w | 83 +++++-- .../Chapter 5/Markdown Phase II.w | 1 + .../Chapter 5/Markdown Rendering.w | 8 + foundation-module/Chapter 5/Markdown.w | 1 + 15 files changed, 334 insertions(+), 215 deletions(-) diff --git a/docs/foundation-module/4-chr.html b/docs/foundation-module/4-chr.html index 3dd3b889..da187b04 100644 --- a/docs/foundation-module/4-chr.html +++ b/docs/foundation-module/4-chr.html @@ -55,7 +55,7 @@

§1. Character classes.

-wchar_t Characters::tolower(wchar_t c) {
+wchar_t Characters::tolower(wchar_t c) {
     return (wchar_t) tolower((int) c);
 }
 wchar_t Characters::toupper(wchar_t c) {
diff --git a/docs/foundation-module/4-pm.html b/docs/foundation-module/4-pm.html
index 8069d20f..a70e92ba 100644
--- a/docs/foundation-module/4-pm.html
+++ b/docs/foundation-module/4-pm.html
@@ -204,7 +204,7 @@ 

-match_results Regexp::create_mr(void) {
+match_results Regexp::create_mr(void) {
     match_results mr;
     mr.no_matched_texts = 0;
     for (int i=0; i<MAX_BRACKETED_SUBEXPRESSIONS; i++) {
@@ -214,7 +214,7 @@ 

return mr; } -void Regexp::dispose_of(match_results *mr) { +void Regexp::dispose_of(match_results *mr) { if (mr) { for (int i=0; i<MAX_BRACKETED_SUBEXPRESSIONS; i++) if (mr->exp[i]) { @@ -229,7 +229,7 @@

-int Regexp::match(match_results *mr, text_stream *text, wchar_t *pattern) {
+int Regexp::match(match_results *mr, text_stream *text, wchar_t *pattern) {
     if (mr) Regexp::prepare(mr);
     int rv = (Regexp::match_r(mr, text, pattern, NULL, FALSE) >= 0)?TRUE:FALSE;
     if ((mr) && (rv == FALSE)) Regexp::dispose_of(mr);
diff --git a/docs/foundation-module/4-sm.html b/docs/foundation-module/4-sm.html
index b637489d..7ada1336 100644
--- a/docs/foundation-module/4-sm.html
+++ b/docs/foundation-module/4-sm.html
@@ -88,7 +88,7 @@ 

-text_stream *Str::new(void) {
+text_stream *Str::new(void) {
     return Str::new_with_capacity(32);
 }
 
@@ -108,7 +108,7 @@ 

-text_stream *Str::duplicate(text_stream *E) {
+text_stream *Str::duplicate(text_stream *E) {
     if (E == NULL) return Str::new();
     text_stream *S = CREATE(text_stream);
     if (Streams::open_to_memory(S, Str::len(E)+4)) {
@@ -198,7 +198,7 @@ 

-int Str::len(text_stream *S) {
+int Str::len(text_stream *S) {
     return Streams::get_position(S);
 }
 
@@ -282,7 +282,7 @@

return Streams::get_char_at_index(P.S, P.index); } -wchar_t Str::get_at(text_stream *S, int index) { +wchar_t Str::get_at(text_stream *S, int index) { if ((S == NULL) || (index < 0)) return 0; return Streams::get_char_at_index(S, index); } @@ -291,7 +291,7 @@

return Str::get(Str::at(S, 0)); } -wchar_t Str::get_last_char(text_stream *S) { +wchar_t Str::get_last_char(text_stream *S) { int L = Str::len(S); if (L == 0) return 0; return Str::get(Str::at(S, L-1)); @@ -312,14 +312,14 @@

Streams::put_char_at_index(P.S, P.index, C); } -void Str::put_at(text_stream *S, int index, wchar_t C) { +void Str::put_at(text_stream *S, int index, wchar_t C) { Str::put(Str::at(S, index), C); }

§15. Truncation.

-void Str::clear(text_stream *S) {
+void Str::clear(text_stream *S) {
     Str::truncate(S, 0);
 }
 
@@ -411,12 +411,12 @@ 

-int Str::eq(text_stream *S1, text_stream *S2) {
+int Str::eq(text_stream *S1, text_stream *S2) {
     if (Str::cmp(S1, S2) == 0) return TRUE;
     return FALSE;
 }
 
-int Str::eq_insensitive(text_stream *S1, text_stream *S2) {
+int Str::eq_insensitive(text_stream *S1, text_stream *S2) {
     if ((Str::len(S1) == Str::len(S2)) && (Str::cmp_insensitive(S1, S2) == 0)) return TRUE;
     return FALSE;
 }
@@ -505,7 +505,7 @@ 

return TRUE; } -int Str::begins_with(text_stream *S1, text_stream *S2) { +int Str::begins_with(text_stream *S1, text_stream *S2) { return Str::prefix_eq(S1, S2, Str::len(S2)); } @@ -563,7 +563,7 @@

§23. White space.

-int Str::is_whitespace(text_stream *S) {
+int Str::is_whitespace(text_stream *S) {
     LOOP_THROUGH_TEXT(pos, S)
         if (Characters::is_space_or_tab(Str::get(pos)) == FALSE)
             return FALSE;
@@ -574,7 +574,7 @@ 

-void Str::trim_white_space(text_stream *S) {
+void Str::trim_white_space(text_stream *S) {
     int len = Str::len(S), i = 0, j = 0;
     string_position F = Str::start(S);
     LOOP_THROUGH_TEXT(P, S) {
@@ -632,7 +632,7 @@ 

Str::delete_nth_character(S, 0); } -void Str::delete_last_character(text_stream *S) { +void Str::delete_last_character(text_stream *S) { if (Str::len(S) > 0) Str::truncate(S, Str::len(S) - 1); } diff --git a/docs/foundation-module/5-htm.html b/docs/foundation-module/5-htm.html index dfd9ed02..4f0e3f82 100644 --- a/docs/foundation-module/5-htm.html +++ b/docs/foundation-module/5-htm.html @@ -581,7 +581,7 @@

-void HTML::begin_plain_html_table(OUTPUT_STREAM) {
+void HTML::begin_plain_html_table(OUTPUT_STREAM) {
     HTML::begin_html_table(OUT, NULL, FALSE, 0, 0, 0, 0, 0);
 }
 
@@ -618,7 +618,7 @@ 

HTML_OPEN_WITH("table", "%S", tab); DISCARD_TEXT(tab) } -void HTML::first_html_column(OUTPUT_STREAM, int width) { +void HTML::first_html_column(OUTPUT_STREAM, int width) { HTML_OPEN("tr"); if (width > 0) HTML_OPEN_WITH("td", "align=\"left\" valign=\"top\" width=\"%d\"", width) else HTML_OPEN_WITH("td", "align=\"left\" valign=\"top\""); @@ -655,7 +655,7 @@

HTML_OPEN_WITH("td", "%S", col); DISCARD_TEXT(col) } -void HTML::next_html_column(OUTPUT_STREAM, int width) { +void HTML::next_html_column(OUTPUT_STREAM, int width) { WRITE("&nbsp;&nbsp;&nbsp;&nbsp;"); HTML_CLOSE("td"); if (width > 0) HTML_OPEN_WITH("td", "align=\"left\" valign=\"top\" width=\"%d\"", width) @@ -702,11 +702,11 @@

if (width > 0) HTML_OPEN_WITH("td", "align=\"right\" valign=\"top\" width=\"%d\"", width) else HTML_OPEN_WITH("td", "align=\"right\" valign=\"top\""); } -void HTML::end_html_row(OUTPUT_STREAM) { +void HTML::end_html_row(OUTPUT_STREAM) { HTML_CLOSE("td"); HTML_CLOSE("tr"); } -void HTML::end_html_table(OUTPUT_STREAM) { +void HTML::end_html_table(OUTPUT_STREAM) { HTML_CLOSE("table"); }

@@ -939,14 +939,14 @@

§26. Spans by class.

-void HTML::begin_span(OUTPUT_STREAM, text_stream *class_name) {
+void HTML::begin_span(OUTPUT_STREAM, text_stream *class_name) {
     if (Str::len(class_name) > 0) {
         HTML_OPEN_WITH("span", "class=\"%S\"", class_name);
     } else {
         HTML_OPEN("span");
     }
 }
-void HTML::end_span(OUTPUT_STREAM) {
+void HTML::end_span(OUTPUT_STREAM) {
     HTML_CLOSE("span");
 }
 
diff --git a/docs/foundation-module/5-im.html b/docs/foundation-module/5-im.html index b42d5de1..41cd55dd 100644 --- a/docs/foundation-module/5-im.html +++ b/docs/foundation-module/5-im.html @@ -50,7 +50,7 @@

A form of Markdown adapted to the needs of the Inform tools.

-
+

§1. The Inform tools use something close to, but not quite the same as, GitHub-flavored Markdown (GFM). There are minor syntactic differences, but @@ -60,7 +60,7 @@

 markdown_variation *Inform_flavoured_Markdown = NULL;
 
-markdown_variation *InformFlavouredMarkdown::variation(void) {
+markdown_variation *InformFlavouredMarkdown::variation(void) {
     if (Inform_flavoured_Markdown) return Inform_flavoured_Markdown;
     Inform_flavoured_Markdown = MarkdownVariations::new(I"Inform-flavoured Markdown");
 
@@ -588,9 +588,9 @@ 

WRITE("&nbsp;Example&nbsp;"); HTML::end_span(OUT); HTML::begin_span(OUT, I"indexblack"); - InformFlavouredMarkdown::render_text(OUT, E->name); + InformFlavouredMarkdown::render_text(OUT, E->name); HTML_TAG("br"); - InformFlavouredMarkdown::render_text(OUT, E->description); + InformFlavouredMarkdown::render_text(OUT, E->description); HTML::end_span(OUT); HTML_CLOSE("b"); if (Str::len(link) > 0) HTML_CLOSE("a"); @@ -666,6 +666,7 @@

§1.21. Phrase definition boxes.

enum PHRASE_DEFN_BOXES_MARKDOWNFEATURE
+enum PHRASE_HEADER_MIT
 

§1.22. Add the phrase defn boxes feature1.22 =

@@ -673,37 +674,87 @@

     markdown_feature *pd = MarkdownVariations::new_feature(I"phrase defn boxes",
         PHRASE_DEFN_BOXES_MARKDOWNFEATURE);
+    METHOD_ADD(pd, POST_PHASE_I_MARKDOWN_MTID,
+        InformFlavouredMarkdown::PD_intervene_after_Phase_I);
     METHOD_ADD(pd, RENDER_MARKDOWN_MTID, InformFlavouredMarkdown::PD_render);
     MarkdownVariations::add_feature(Inform_flavoured_Markdown, PHRASE_DEFN_BOXES_MARKDOWNFEATURE);
+    Markdown::new_leaf_block_type(PHRASE_HEADER_MIT, I"PHRASE_HEADER");
 
  • This code is used in §1.
-

§1.23.

+

§2.

-int InformFlavouredMarkdown::PD_render(markdown_feature *feature, text_stream *OUT,
-    markdown_item *md, int mode) {
+void InformFlavouredMarkdown::PD_intervene_after_Phase_I(markdown_feature *feature,
+    markdown_item *md, md_links_dictionary *link_references) {
     if (md->type == BLOCK_QUOTE_MIT) {
         if ((md->down) && (md->down->type == PARAGRAPH_MIT)) {
             match_results mr = Regexp::create_mr();
-            if ((Regexp::match(&mr, md->down->stashed, L"phrase: *{(%c*?)} *(%c+?)\n(%c*)")) ||
-                (Regexp::match(&mr, md->down->stashed, L"(phrase): *(%c+?)\n(%c*)"))) {
-                HTML_OPEN_WITH("div", "class=\"definition\"");
-                HTML_OPEN_WITH("p", "class=\"defnprototype\"");
-                WRITE("%S", mr.exp[1]);
-                HTML_CLOSE("p");
-                HTML_TAG("br");
-                markdown_item *remainder =
-                    Markdown::parse_inline_extended(mr.exp[2], InformFlavouredMarkdown::variation());
-                Markdown::render_extended(OUT, remainder, InformFlavouredMarkdown::variation());
-                for (markdown_item *ch = md->down->next; ch; ch = ch->next)
-                    Markdown::render_extended(OUT, ch, InformFlavouredMarkdown::variation());
-                HTML_CLOSE("div");
-                Regexp::dispose_of(&mr);
-                return TRUE;
+            if ((Regexp::match(&mr, md->down->stashed, L"phrase: *%{(%c*?)%} *(%c+?)")) ||
+                (Regexp::match(&mr, md->down->stashed, L"(phrase): *(%c+?)"))) {
+                markdown_item *join_to = NULL;
+                TEMPORARY_TEXT(phrase)
+                for (int i=0; i<Str::len(mr.exp[1]); i++) {
+                    if (Str::get_at(mr.exp[1], i) == '&') {
+                        Insert a phrase header here2.1;
+                        Str::clear(phrase);
+                    } else {
+                        PUT_TO(phrase, Str::get_at(mr.exp[1], i));
+                    }
+                }
+                Insert a phrase header here2.1;
+                DISCARD_TEXT(phrase)
             }
-            Regexp::dispose_of(&mr);
         }
     }
+    for (markdown_item *ch = md->down; ch; ch=ch->next) {
+        InformFlavouredMarkdown::PD_intervene_after_Phase_I(feature, ch, link_references);
+    }
+}
+
+

§2.1. Insert a phrase header here2.1 = +

+ +
+    Str::trim_white_space(phrase);
+    if (Str::len(phrase) > 0) {
+        markdown_item *ph = Markdown::new_item(PHRASE_HEADER_MIT);
+        ph->stashed = Str::duplicate(phrase);
+        if (join_to == NULL) { ph->next = md->down->next; md->down = ph; }
+        else { ph->next = join_to->next; join_to->next = ph; }
+        join_to = ph;
+
+        markdown_item *im = Markdown::new_item(INDEX_MARKER_MIT);
+        im->stashed = Str::new();
+        text_stream *category = I"+to+";
+        if (Str::begins_with(phrase, I"if ")) category = I"+toif+";
+        if (Str::begins_with(phrase, I"say ")) category = I"+tosay+";
+        WRITE_TO(im->stashed, "%S%S", category, phrase);
+        if (Str::get_last_char(im->stashed) == ':')
+            Str::delete_last_character(im->stashed);
+        im->details = 1;
+        im->next = join_to->next; join_to->next = im;
+        join_to = im;
+    }
+
+
  • This code is used in §2 (twice).
+

§1.23.

+ +
+int InformFlavouredMarkdown::PD_render(markdown_feature *feature, text_stream *OUT,
+    markdown_item *md, int mode) {
+    if ((md->type == BLOCK_QUOTE_MIT) && (md->down) && (md->down->type == PHRASE_HEADER_MIT)) {
+        HTML_OPEN_WITH("div", "class=\"definition\"");
+        for (markdown_item *ch = md->down; ch; ch = ch->next)
+            Markdown::render_extended(OUT, ch, InformFlavouredMarkdown::variation());
+        HTML_CLOSE("div");
+        return TRUE;
+    }
+    if (md->type == PHRASE_HEADER_MIT) {
+        HTML_OPEN_WITH("p", "class=\"defnprototype\"");
+        MDRenderer::stream(OUT, md->stashed, mode);
+        HTML_CLOSE("p");
+        return TRUE;
+    }
     return FALSE;
 }
 
@@ -746,7 +797,7 @@

§1.28.

-void InformFlavouredMarkdown::HM_intervene_after_Phase_I(markdown_feature *feature,
+void InformFlavouredMarkdown::HM_intervene_after_Phase_I(markdown_feature *feature,
     markdown_item *md, md_links_dictionary *link_references) {
     if ((md->type == HEADING_MIT) && (Markdown::get_heading_level(md) <= 2)) {
         text_stream *line = md->stashed;
@@ -789,7 +840,7 @@ 

§1.31.

-void InformFlavouredMarkdown::PG_intervene_after_Phase_I(markdown_feature *feature,
+void InformFlavouredMarkdown::PG_intervene_after_Phase_I(markdown_feature *feature,
     markdown_item *md, md_links_dictionary *link_references) {
     if (md->type == PARAGRAPH_MIT) {
         text_stream *line = md->stashed;
@@ -836,7 +887,7 @@ 

IFM_gatekeeper = gatekeeper; } -int InformFlavouredMarkdown::PG_render(markdown_feature *feature, text_stream *OUT, +int InformFlavouredMarkdown::PG_render(markdown_feature *feature, text_stream *OUT, markdown_item *md, int mode) { if (md->type == GATE_MIT) { int decision = FALSE; @@ -860,29 +911,29 @@

markdown_feature *sc = MarkdownVariations::new_feature(I"Inform syntax-colouring", INFORM_SYNTAX_COLOURING_MARKDOWNFEATURE); - METHOD_ADD(sc, RENDER_MARKDOWN_MTID, InformFlavouredMarkdown::SC_render); + METHOD_ADD(sc, RENDER_MARKDOWN_MTID, InformFlavouredMarkdown::SC_render); MarkdownVariations::add_feature(Inform_flavoured_Markdown, INFORM_SYNTAX_COLOURING_MARKDOWNFEATURE);

  • This code is used in §1.
-

§2.

+

§3.

-int InformFlavouredMarkdown::SC_render(markdown_feature *feature, text_stream *OUT,
+int InformFlavouredMarkdown::SC_render(markdown_feature *feature, text_stream *OUT,
     markdown_item *md, int mode) {
     switch (md->type) {
-        case CODE_MIT:       Render a code snippet2.1; return TRUE;
-        case CODE_BLOCK_MIT: Render a code block2.2;   return TRUE;
+        case CODE_MIT:       Render a code snippet3.1; return TRUE;
+        case CODE_BLOCK_MIT: Render a code block3.2;   return TRUE;
     }
     return FALSE;
 }
 
-

§2.1. We consider a snippet backticked once as being Inform source text, and +

§3.1. We consider a snippet backticked once as being Inform source text, and twice or more as being in some other more conventional programming language. But this affects only the CSS class applied to it.

-

Render a code snippet2.1 = +

Render a code snippet3.1 =

@@ -901,12 +952,12 @@ 

MDRenderer::slice(OUT, md, mode); if (mode & TAGS_MDRMODE) HTML_CLOSE("code");

-
  • This code is used in §2.
-

§2.2. As is customary in Markdown, the first word of the info string on a code +

  • This code is used in §3.
+

§3.2. As is customary in Markdown, the first word of the info string on a code block (if there is one) names the language in use.

-

Render a code block2.2 = +

Render a code block3.2 =

@@ -922,24 +973,24 @@ 

md->from = 0; md->to = Str::len(language_text) - 1; MDRenderer::slice(language, md, mode | ENTITIES_MDRMODE); } - Decide on a language if none was supplied2.2.1; + Decide on a language if none was supplied3.2.1; if ((Str::eq_insensitive(language, I"inform")) || (Str::eq_insensitive(language, I"inform7"))) { - Render as Inform 7 source text2.2.2; + Render as Inform 7 source text3.2.2; } else if (Str::eq_insensitive(language, I"problems")) { - Render as problem message2.2.3; + Render as problem message3.2.3; } else { - Render as some other programming language content2.2.4; + Render as some other programming language content3.2.4; } DISCARD_TEXT(language_text) DISCARD_TEXT(language)

-
  • This code is used in §2.
-

§2.2.1. Usually Inform, but if one line looks like a traditional IF prompt character, +

  • This code is used in §3.
+

§3.2.1. Usually Inform, but if one line looks like a traditional IF prompt character, use "transcript" instead.

-

Decide on a language if none was supplied2.2.1 = +

Decide on a language if none was supplied3.2.1 =

@@ -953,8 +1004,8 @@ 

if (Str::len(language) == 0) WRITE_TO(language, "inform"); }

-
  • This code is used in §2.2.
-

§2.2.2. Render as Inform 7 source text2.2.2 = +

  • This code is used in §3.2.
+

§3.2.2. Render as Inform 7 source text3.2.2 =

@@ -1000,23 +1051,23 @@ 

TEMPORARY_TEXT(line_colouring) for (int k=0; k<Str::len(md->stashed); k++) { if (Str::get_at(md->stashed, k) == '\n') { - Render line2.2.2.1; + Render line3.2.2.1; Str::clear(line); Str::clear(line_colouring); } else { PUT_TO(line, Str::get_at(md->stashed, k)); PUT_TO(line_colouring, Str::get_at(colouring, k)); } - if ((k == Str::len(md->stashed) - 1) && (Str::len(line) > 0)) Render line2.2.2.1; + if ((k == Str::len(md->stashed) - 1) && (Str::len(line) > 0)) Render line3.2.2.1; } HTML_CLOSE("span"); - if (tabulating) End I7 table in extension documentation2.2.2.2; + if (tabulating) End I7 table in extension documentation3.2.2.2; HTML_CLOSE("blockquote"); DISCARD_TEXT(line) DISCARD_TEXT(line_colouring)

-
  • This code is used in §2.2.
-

§2.2.2.1. Render line2.2.2.1 = +

  • This code is used in §3.2.
+

§3.2.2.1. Render line3.2.2.1 =

@@ -1027,29 +1078,29 @@ 

Regexp::dispose_of(&mr); if (tabular) { if (tabulating) { - Begin new row of I7 table in extension documentation2.2.2.1.4; + Begin new row of I7 table in extension documentation3.2.2.1.4; } else { - Begin I7 table in extension documentation2.2.2.1.1; + Begin I7 table in extension documentation3.2.2.1.1; tabulating = TRUE; } int cell_from = 0, cell_to = 0, i = 0; - Begin table cell for I7 table in extension documentation2.2.2.1.3; + Begin table cell for I7 table in extension documentation3.2.2.1.3; for (; i<Str::len(line); i++) { if (Str::get_at(line, i) == '\t') { - End table cell for I7 table in extension documentation2.2.2.1.2; + End table cell for I7 table in extension documentation3.2.2.1.2; while (Str::get_at(line, i) == '\t') i++; - Begin table cell for I7 table in extension documentation2.2.2.1.3; + Begin table cell for I7 table in extension documentation3.2.2.1.3; i--; } else { cell_to++; } } - End table cell for I7 table in extension documentation2.2.2.1.2; - End row of I7 table in extension documentation2.2.2.1.5; + End table cell for I7 table in extension documentation3.2.2.1.2; + End row of I7 table in extension documentation3.2.2.1.5; } else { if (line_count > 1) HTML_TAG("br"); if (tabulating) { - End I7 table in extension documentation2.2.2.2; + End I7 table in extension documentation3.2.2.2; tabulating = FALSE; } int indentation = 0; @@ -1059,17 +1110,17 @@

else if (Str::get_at(line, z) == '\t') { indentation++; spaces = 0; } else break; for (int n=0; n<indentation; n++) WRITE("&nbsp;&nbsp;&nbsp;&nbsp;"); - InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring, + InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring, z, Str::len(line), mode); } WRITE("\n");

-
  • This code is used in §2.2.2 (twice).
-

§2.2.2.1.1. Unsurprisingly, I7 tables are set (after their titling lines) as HTML tables, +

  • This code is used in §3.2.2 (twice).
+

§3.2.2.1.1. Unsurprisingly, I7 tables are set (after their titling lines) as HTML tables, and this is fiddly but elementary in the usual way of HTML tables:

-

Begin I7 table in extension documentation2.2.2.1.1 = +

Begin I7 table in extension documentation3.2.2.1.1 =

@@ -1078,48 +1129,48 @@ 

HTML::begin_plain_html_table(OUT); HTML::first_html_column(OUT, 0);

- -

§2.2.2.1.2. End table cell for I7 table in extension documentation2.2.2.1.2 = +

+

§3.2.2.1.2. End table cell for I7 table in extension documentation3.2.2.1.2 =

-    InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring,
+    InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring,
         cell_from, cell_to, mode);
     HTML::end_span(OUT);
     HTML::next_html_column(OUT, 0);
 
- -

§2.2.2.1.3. Begin table cell for I7 table in extension documentation2.2.2.1.3 = +

+

§3.2.2.1.3. Begin table cell for I7 table in extension documentation3.2.2.1.3 =

     cell_from = i; cell_to = cell_from;
     HTML::begin_span(OUT, I"indexdullblue");
 
- -

§2.2.2.1.4. Begin new row of I7 table in extension documentation2.2.2.1.4 = +

+

§3.2.2.1.4. Begin new row of I7 table in extension documentation3.2.2.1.4 =

     HTML::first_html_column(OUT, 0);
 
- -

§2.2.2.1.5. End row of I7 table in extension documentation2.2.2.1.5 = +

+

§3.2.2.1.5. End row of I7 table in extension documentation3.2.2.1.5 =

     HTML::end_html_row(OUT);
 
- -

§2.2.2.2. End I7 table in extension documentation2.2.2.2 = +

+

§3.2.2.2. End I7 table in extension documentation3.2.2.2 =

     HTML::end_html_table(OUT);
     HTML::begin_span(OUT, I"indexdullblue");
 
- -

§2.2.3. Render as problem message2.2.3 = +

+

§3.2.3. Render as problem message3.2.3 =

@@ -1127,12 +1178,12 @@ 

HTML_OPEN_WITH("div", "class=\"extract-problems\""); if (mode & TAGS_MDRMODE) HTML_OPEN("blockquote"); for (int k=0; k<Str::len(md->stashed); k++) - MDRenderer::char(OUT, Str::get_at(md->stashed, k), mode); + MDRenderer::char(OUT, Str::get_at(md->stashed, k), mode); if (mode & TAGS_MDRMODE) HTML_CLOSE("blockquote"); if (mode & TAGS_MDRMODE) HTML_CLOSE("div");

-
  • This code is used in §2.2.
-

§2.2.4. Render as some other programming language content2.2.4 = +

  • This code is used in §3.2.
+

§3.2.4. Render as some other programming language content3.2.4 =

@@ -1156,14 +1207,14 @@ 

TEMPORARY_TEXT(line_colouring) for (int k=0; k<Str::len(md->stashed); k++) { if (Str::get_at(md->stashed, k) == '\n') { - Render line as code2.2.4.1; + Render line as code3.2.4.1; Str::clear(line); Str::clear(line_colouring); } else { PUT_TO(line, Str::get_at(md->stashed, k)); } if ((k == Str::len(md->stashed) - 1) && (Str::len(line) > 0)) - Render line as code2.2.4.1; + Render line as code3.2.4.1; } DISCARD_TEXT(line) DISCARD_TEXT(line_colouring) @@ -1173,21 +1224,21 @@

if (mode & TAGS_MDRMODE) HTML_CLOSE("div"); }

- -

§2.2.4.1. Render line as code2.2.4.1 = +

  • This code is used in §3.2.
+

§3.2.4.1. Render line as code3.2.4.1 =

     if (pl) Painter::syntax_colour(pl, NULL, line, line_colouring, FALSE);
-    InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring,
+    InformFlavouredMarkdown::syntax_coloured_code(OUT, line, line_colouring,
         0, Str::len(line), mode);
     if (mode & TAGS_MDRMODE) WRITE("<br>"); else WRITE(" ");
 
-
  • This code is used in §2.2.4 (twice).
-

§3.

+
  • This code is used in §3.2.4 (twice).
+

§4.

-void InformFlavouredMarkdown::syntax_coloured_code(OUTPUT_STREAM, text_stream *text,
+void InformFlavouredMarkdown::syntax_coloured_code(OUTPUT_STREAM, text_stream *text,
     text_stream *colouring, int from, int to, int mode) {
     wchar_t current_col = 0;
     for (int i=from; i<to; i++) {
@@ -1212,16 +1263,16 @@ 

HTML_OPEN_WITH("span", "class=\"%S\"", span_class); current_col = col; } - MDRenderer::char(OUT, c, mode); + MDRenderer::char(OUT, c, mode); } if (current_col) HTML_CLOSE("span"); }

-

§4. Small inline texts. This utility function parses and renders short inline content only: +

§5. Small inline texts. This utility function parses and renders short inline content only:

-void InformFlavouredMarkdown::render_text(OUTPUT_STREAM, text_stream *text) {
+void InformFlavouredMarkdown::render_text(OUTPUT_STREAM, text_stream *text) {
     markdown_item *md = Markdown::parse_inline(text);
     HTML_OPEN_WITH("span", "class=\"markdowncontent\"");
     Markdown::render_extended(OUT, md, InformFlavouredMarkdown::variation());
diff --git a/docs/foundation-module/5-mpi2.html b/docs/foundation-module/5-mpi2.html
index 5c3fb8fc..23a07b75 100644
--- a/docs/foundation-module/5-mpi2.html
+++ b/docs/foundation-module/5-mpi2.html
@@ -267,6 +267,7 @@ 

     int name_inversion = FALSE;
+
     if (Str::get_first_char(lemma) == '@') {
         Str::delete_first_character(lemma);
         name_inversion = TRUE;
diff --git a/docs/foundation-module/5-mr.html b/docs/foundation-module/5-mr.html
index c7274182..37d5ff2b 100644
--- a/docs/foundation-module/5-mr.html
+++ b/docs/foundation-module/5-mr.html
@@ -115,7 +115,7 @@ 

break; case LINE_BREAK_MIT: if (mode & TAGS_MDRMODE) WRITE("<br />\n"); break; - case SOFT_BREAK_MIT: MDRenderer::char(OUT, '\n', mode); + case SOFT_BREAK_MIT: MDRenderer::char(OUT, '\n', mode); break; case EMPHASIS_MIT: if (mode & TAGS_MDRMODE) HTML_OPEN("em"); Recurse2.13; @@ -499,7 +499,7 @@

-void MDRenderer::slice(OUTPUT_STREAM, markdown_item *md, int mode) {
+void MDRenderer::slice(OUTPUT_STREAM, markdown_item *md, int mode) {
     if (md) {
         int angles = 0;
         for (int i=md->from; i<=md->to; i++) {
@@ -522,27 +522,36 @@ 

DISCARD_TEXT(entity) if (valid) { if (A == 0) A = 0xFFFD; - MDRenderer::char(OUT, A, mode); - if (B) MDRenderer::char(OUT, B, mode); + MDRenderer::char(OUT, A, mode); + if (B) MDRenderer::char(OUT, B, mode); i = at - 1; continue; } } } if ((c == '<') && (angles++ == 0) && (mode & FILTERED_MDRMODE)) - MDRenderer::char(OUT, c, 0); + MDRenderer::char(OUT, c, 0); else - MDRenderer::char(OUT, c, mode); + MDRenderer::char(OUT, c, mode); } } }

-

§4. Down at the individual character level, there are three mutually exclusive +

§4. Alternatively: +

+ +
+void MDRenderer::stream(OUTPUT_STREAM, text_stream *stream, int mode) {
+    for (int i=0; i<Str::len(stream); i++)
+        MDRenderer::char(OUT, Str::get_at(stream, i), mode);
+}
+
+

§5. Down at the individual character level, there are three mutually exclusive ways to render characters: they all agree on ASCII digits and letters.

-void MDRenderer::char(OUTPUT_STREAM, wchar_t c, int mode) {
+void MDRenderer::char(OUTPUT_STREAM, wchar_t c, int mode) {
     if (mode & TOLOWER_MDRMODE) c = Characters::tolower(c);
     if (mode & RAW_MDRMODE) {
         PUT(c);
@@ -584,7 +593,7 @@ 

} }

-

§5. CommonMark likes hexadecimal escapes in URIs to use upper case A to F; +

§6. CommonMark likes hexadecimal escapes in URIs to use upper case A to F; I suspect Web browsers don't care, but it's nice to comply exactly with the CommonMark test examples, so:

@@ -592,8 +601,8 @@

define MARKDOWN_URI_HEX(x) {
         unsigned int z = (unsigned int) x;
         PUT('%');
-        MDRenderer::hex_digit(OUT, z >> 4);
-        MDRenderer::hex_digit(OUT, z & 0x0f);
+        MDRenderer::hex_digit(OUT, z >> 4);
+        MDRenderer::hex_digit(OUT, z & 0x0f);
     }
 
diff --git a/docs/foundation-module/5-mrk.html b/docs/foundation-module/5-mrk.html
index cf71a3f8..943cc261 100644
--- a/docs/foundation-module/5-mrk.html
+++ b/docs/foundation-module/5-mrk.html
@@ -172,7 +172,7 @@ 

return Markdown::parse_general(NULL, text, dict, TRUE, FALSE, NULL); } -markdown_item *Markdown::parse_inline(text_stream *text) { +markdown_item *Markdown::parse_inline(text_stream *text) { return Markdown::parse_general(NULL, text, NULL, FALSE, TRUE, NULL); } @@ -208,7 +208,7 @@

return Markdown::parse_general(NULL, text, dict, TRUE, FALSE, variation); } -markdown_item *Markdown::parse_inline_extended(text_stream *text, +markdown_item *Markdown::parse_inline_extended(text_stream *text, markdown_variation *variation) { return Markdown::parse_general(NULL, text, NULL, FALSE, TRUE, variation); } @@ -233,7 +233,7 @@

-markdown_item *Markdown::parse_general(markdown_item *tree, text_stream *text,
+markdown_item *Markdown::parse_general(markdown_item *tree, text_stream *text,
     md_links_dictionary *dict, int phase_I, int phase_II, markdown_variation *variation) {
     if (variation == NULL) variation = MarkdownVariations::CommonMark();
     if (tracing_Markdown_parser) PRINT("Syntax variation: %S\n", variation->name);
@@ -325,7 +325,7 @@ 

MDRenderer::render_extended(OUT, tree, MarkdownVariations::CommonMark()); } -void Markdown::render_extended(OUTPUT_STREAM, markdown_item *tree, +void Markdown::render_extended(OUTPUT_STREAM, markdown_item *tree, markdown_variation *variation) { MDRenderer::render_extended(OUT, tree, variation); } @@ -433,13 +433,13 @@

markdown_type_metadata_registry[mit].is_quasiplainish = FALSE; } -void Markdown::new_container_block_type(int mit, text_stream *name) { +void Markdown::new_container_block_type(int mit, text_stream *name) { Markdown::new_item_type(mit, name); markdown_type_metadata_registry[mit].is_container = TRUE; markdown_type_metadata_registry[mit].is_block = TRUE; } -void Markdown::new_leaf_block_type(int mit, text_stream *name) { +void Markdown::new_leaf_block_type(int mit, text_stream *name) { Markdown::new_item_type(mit, name); markdown_type_metadata_registry[mit].is_block = TRUE; } @@ -456,13 +456,13 @@

markdown_type_metadata_registry[mit].is_quasiplainish = TRUE; } -void Markdown::new_quasiplainish_inline_type(int mit, text_stream *name) { +void Markdown::new_quasiplainish_inline_type(int mit, text_stream *name) { Markdown::new_item_type(mit, name); markdown_type_metadata_registry[mit].is_inline = TRUE; markdown_type_metadata_registry[mit].is_quasiplainish = TRUE; } -void Markdown::create_item_types(void) { +void Markdown::create_item_types(void) { for (int mit = 1; mit <= NO_DEFINED_MIT_VALUES; mit++) Markdown::new_item_type(mit, I"?UNDEFINED"); @@ -509,7 +509,7 @@

Markdown::new_inline_type(XMPP_AUTOLINK_MIT, I"XMPP_AUTOLINK"); } -text_stream *Markdown::item_type_name(int t) { +text_stream *Markdown::item_type_name(int t) { if ((t<1) || (t>NO_DEFINED_MIT_VALUES)) return I"<UNKNOWN>"; return markdown_type_metadata_registry[t].name; } @@ -530,7 +530,7 @@

-int Markdown::item_type_inline(int t) {
+int Markdown::item_type_inline(int t) {
     if ((t<1) || (t>NO_DEFINED_MIT_VALUES)) return FALSE;
     return markdown_type_metadata_registry[t].is_inline;
 }
@@ -539,7 +539,7 @@ 

-int Markdown::plainish(markdown_item *md) {
+int Markdown::plainish(markdown_item *md) {
     if (md) return Markdown::item_type_plainish(md->type);
     return FALSE;
 }
@@ -553,7 +553,7 @@ 

-int Markdown::quasi_plainish(markdown_item *md) {
+int Markdown::quasi_plainish(markdown_item *md) {
     if (md) return Markdown::item_type_quasi_plainish(md->type);
     return FALSE;
 }
@@ -599,7 +599,7 @@ 

} markdown_item; int md_ids = 1; -markdown_item *Markdown::new_item(int type) { +markdown_item *Markdown::new_item(int type) { markdown_item *md = CREATE(markdown_item); md->type = type; @@ -628,7 +628,7 @@

-markdown_item *Markdown::deep_copy(markdown_item *md) {
+markdown_item *Markdown::deep_copy(markdown_item *md) {
     if (md == NULL) internal_error("cannot copy null node");
     if (Markdown::item_type_inline(md->type) == FALSE)
         internal_error("can only copy inline nodes");
@@ -638,6 +638,7 @@ 

} copied->from = md->from; copied->to = md->to; + copied->stashed = md->stashed; copied->copied_from = md; for (markdown_item *c = md->down; c; c = c->next) Markdown::add_to(Markdown::deep_copy(c), copied); @@ -648,7 +649,7 @@

-void Markdown::add_to(markdown_item *md, markdown_item *owner) {
+void Markdown::add_to(markdown_item *md, markdown_item *owner) {
     md->next = NULL;
     if (owner->down == NULL) { owner->down = md; return; }
     for (markdown_item *ch = owner->down; ch; ch = ch->next)
@@ -659,12 +660,12 @@ 

-int Markdown::get_heading_level(markdown_item *md) {
+int Markdown::get_heading_level(markdown_item *md) {
     if ((md == NULL) || (md->type != HEADING_MIT)) return 0;
     return md->details;
 }
 
-void Markdown::set_heading_level(markdown_item *md, int L) {
+void Markdown::set_heading_level(markdown_item *md, int L) {
     if ((md == NULL) || (md->type != HEADING_MIT)) internal_error("not a heading");
     if ((L < 1) || (L > 6)) internal_error("bad heading level");
     md->details = L;
@@ -679,7 +680,7 @@ 

return md->details; } -void Markdown::set_column_count(markdown_item *md, int L) { +void Markdown::set_column_count(markdown_item *md, int L) { if ((md == NULL) || (md->type != TABLE_MIT)) internal_error("not a table"); if (L < 1) internal_error("bad column count"); md->details = L; @@ -689,12 +690,12 @@

-int Markdown::get_alignment(markdown_item *md) {
+int Markdown::get_alignment(markdown_item *md) {
     if ((md == NULL) || (md->type != TABLE_COLUMN_MIT)) return 0;
     return md->details;
 }
 
-void Markdown::set_alignment(markdown_item *md, int L) {
+void Markdown::set_alignment(markdown_item *md, int L) {
     if ((md == NULL) || (md->type != TABLE_COLUMN_MIT)) internal_error("not a table col");
     if ((L < 0) || (L > 3)) internal_error("bad alignment");
     md->details = L;
@@ -704,18 +705,18 @@ 

-int Markdown::get_filtered_state(markdown_item *md) {
+int Markdown::get_filtered_state(markdown_item *md) {
     if ((md == NULL) || (md->type != INLINE_HTML_MIT)) return FALSE;
     return md->details;
 }
 
-void Markdown::set_filtered_state(markdown_item *md, int L) {
+void Markdown::set_filtered_state(markdown_item *md, int L) {
     if ((md == NULL) || (md->type != INLINE_HTML_MIT)) internal_error("not inline HTML");
     if ((L != FALSE) && (L != TRUE)) internal_error("bad filtered state");
     md->details = L;
 }
 
-int Markdown::tag_should_be_filtered(text_stream *tag) {
+int Markdown::tag_should_be_filtered(text_stream *tag) {
     if (Str::eq_insensitive(tag, I"title")) return TRUE;
     if (Str::eq_insensitive(tag, I"textarea")) return TRUE;
     if (Str::eq_insensitive(tag, I"style")) return TRUE;
@@ -732,14 +733,14 @@ 

-int Markdown::get_add_protocol_state(markdown_item *md) {
+int Markdown::get_add_protocol_state(markdown_item *md) {
     if ((md == NULL) ||
         ((md->type != URI_AUTOLINK_MIT) && (md->type != EMAIL_AUTOLINK_MIT) && (md->type != XMPP_AUTOLINK_MIT)))
         return FALSE;
     return md->details;
 }
 
-void Markdown::set_add_protocol_state(markdown_item *md, int L) {
+void Markdown::set_add_protocol_state(markdown_item *md, int L) {
     if ((md == NULL) ||
         ((md->type != URI_AUTOLINK_MIT) && (md->type != EMAIL_AUTOLINK_MIT) && (md->type != XMPP_AUTOLINK_MIT)))
         internal_error("not an autolink");
@@ -751,12 +752,12 @@ 

-int Markdown::get_tick_state(markdown_item *md) {
+int Markdown::get_tick_state(markdown_item *md) {
     if ((md == NULL) || (md->type != TICKBOX_MIT)) return FALSE;
     return md->details;
 }
 
-void Markdown::set_tick_state(markdown_item *md, int L) {
+void Markdown::set_tick_state(markdown_item *md, int L) {
     if ((md == NULL) || (md->type != TICKBOX_MIT)) internal_error("not a tickbox");
     if ((L != FALSE) && (L != TRUE)) internal_error("bad tick state");
     md->details = L;
@@ -767,12 +768,12 @@ 

-int Markdown::get_backtick_count(markdown_item *md) {
+int Markdown::get_backtick_count(markdown_item *md) {
     if ((md == NULL) || (md->type != CODE_MIT)) return FALSE;
     return md->details;
 }
 
-void Markdown::set_backtick_count(markdown_item *md, int L) {
+void Markdown::set_backtick_count(markdown_item *md, int L) {
     if ((md == NULL) || (md->type != CODE_MIT)) internal_error("not a code item");
     if (L < 0) internal_error("bad backtick count");
     md->details = L;
@@ -782,13 +783,13 @@ 

-int Markdown::get_item_number(markdown_item *md) {
+int Markdown::get_item_number(markdown_item *md) {
     if ((md == NULL) || (md->type != ORDERED_LIST_ITEM_MIT)) return 0;
     if (md->details < 0) return -(md->details+1);
     return md->details;
 }
 
-wchar_t Markdown::get_item_flavour(markdown_item *md) {
+wchar_t Markdown::get_item_flavour(markdown_item *md) {
     if ((md == NULL) ||
         ((md->type != ORDERED_LIST_ITEM_MIT) && (md->type != UNORDERED_LIST_ITEM_MIT)))
         return 0;
@@ -799,7 +800,7 @@ 

return (wchar_t) md->details; } -void Markdown::set_item_number_and_flavour(markdown_item *md, int L, wchar_t f) { +void Markdown::set_item_number_and_flavour(markdown_item *md, int L, wchar_t f) { if (md->type == ORDERED_LIST_ITEM_MIT) { if (L < 0) internal_error("bad list item number"); if (f == ')') md->details = L; @@ -821,7 +822,7 @@

-wchar_t Markdown::get_unescaped(md_charpos pos, int offset) {
+wchar_t Markdown::get_unescaped(md_charpos pos, int offset) {
     wchar_t c = Markdown::get_offset(pos, offset);
     int preceding_backslashes = 0;
     while (Markdown::get_offset(pos, offset - 1 - preceding_backslashes) == '\\')
@@ -835,7 +836,7 @@ 

-int Markdown::unescaped_run(md_charpos pos, wchar_t of) {
+int Markdown::unescaped_run(md_charpos pos, wchar_t of) {
     int count = 0;
     while (Markdown::get_unescaped(pos, count) == of) count++;
     if (Markdown::get_unescaped(pos, -1) == of) count = 0;
@@ -845,19 +846,19 @@ 

§31. File markers.

-markdown_item *Markdown::new_volume_marker(text_stream *title) {
+markdown_item *Markdown::new_volume_marker(text_stream *title) {
     markdown_item *md = Markdown::new_item(VOLUME_MIT);
     md->stashed = Str::duplicate(title);
     return md;
 }
 
-markdown_item *Markdown::new_file_marker(filename *F) {
+markdown_item *Markdown::new_file_marker(filename *F) {
     markdown_item *md = Markdown::new_item(FILE_MIT);
     md->user_state = STORE_POINTER_filename(F);
     return md;
 }
 
-filename *Markdown::get_filename(markdown_item *md) {
+filename *Markdown::get_filename(markdown_item *md) {
     if ((md == NULL) || (md->type != FILE_MIT)) return NULL;
     return RETRIEVE_POINTER_filename(md->user_state);
 }
@@ -869,7 +870,7 @@ 

-markdown_item *Markdown::new_slice(int type, text_stream *text, int from, int to) {
+markdown_item *Markdown::new_slice(int type, text_stream *text, int from, int to) {
     markdown_item *md = Markdown::new_item(type);
     md->sliced_from = text;
     md->from = from;
@@ -884,7 +885,7 @@ 

-wchar_t Markdown::get_at(markdown_item *md, int at) {
+wchar_t Markdown::get_at(markdown_item *md, int at) {
     if (md == NULL) return 0;
     if (Str::len(md->sliced_from) == 0) return 0;
     return Str::get_at(md->sliced_from, at);
@@ -896,7 +897,7 @@ 

-int Markdown::width(markdown_item *md) {
+int Markdown::width(markdown_item *md) {
     if (md) {
         int width = 0;
         if (md->type == PLAIN_MIT) {
@@ -938,14 +939,14 @@ 

-md_charpos Markdown::nowhere(void) {
+md_charpos Markdown::nowhere(void) {
     md_charpos pos;
     pos.md = NULL;
     pos.at = -1;
     return pos;
 }
 
-md_charpos Markdown::pos(markdown_item *md, int at) {
+md_charpos Markdown::pos(markdown_item *md, int at) {
     if (md == NULL) return Markdown::nowhere();
     md_charpos pos;
     pos.md = md;
@@ -953,7 +954,7 @@ 

return pos; } -int Markdown::somewhere(md_charpos pos) { +int Markdown::somewhere(md_charpos pos) { if (pos.md) return TRUE; return FALSE; } @@ -962,7 +963,7 @@

-int Markdown::pos_eq(md_charpos A, md_charpos B) {
+int Markdown::pos_eq(md_charpos A, md_charpos B) {
     if ((A.md) && (A.md == B.md) && (A.at == B.at)) return TRUE;
     if ((A.md == NULL) && (B.md == NULL)) return TRUE;
     return FALSE;
@@ -974,7 +975,7 @@ 

-int Markdown::is_in(md_charpos pos, markdown_item *md) {
+int Markdown::is_in(md_charpos pos, markdown_item *md) {
     if ((Markdown::somewhere(pos)) && (md)) {
         if ((md->sliced_from) && (md->sliced_from == pos.md->sliced_from) &&
             (pos.at >= md->from) && (pos.at <= md->to)) return TRUE;
@@ -987,7 +988,7 @@ 

-md_charpos Markdown::left_edge_of(markdown_item *md) {
+md_charpos Markdown::left_edge_of(markdown_item *md) {
     if (md == NULL) return Markdown::nowhere();
     return Markdown::pos(md, md->from);
 }
@@ -998,7 +999,7 @@ 

-md_charpos Markdown::advance(md_charpos pos) {
+md_charpos Markdown::advance(md_charpos pos) {
     if (Markdown::somewhere(pos)) {
         if (pos.at < pos.md->to) { pos.at++; return pos; }
         pos.md = pos.md->next;
@@ -1012,7 +1013,7 @@ 

-md_charpos Markdown::advance_plainish_only(md_charpos pos) {
+md_charpos Markdown::advance_plainish_only(md_charpos pos) {
     if (Markdown::somewhere(pos)) {
         if (pos.at < pos.md->to) { pos.at++; return pos; }
         pos.md = pos.md->next;
@@ -1025,7 +1026,7 @@ 

-md_charpos Markdown::advance_quasi_plainish_only(md_charpos pos) {
+md_charpos Markdown::advance_quasi_plainish_only(md_charpos pos) {
     if (Markdown::somewhere(pos)) {
         if (pos.at < pos.md->to) { pos.at++; return pos; }
         pos.md = pos.md->next;
@@ -1038,21 +1039,21 @@ 

-md_charpos Markdown::advance_up_to(md_charpos pos, md_charpos end) {
+md_charpos Markdown::advance_up_to(md_charpos pos, md_charpos end) {
     if ((Markdown::somewhere(end)) &&
         (pos.md->sliced_from == end.md->sliced_from) && (pos.at >= end.at))
         return Markdown::nowhere();
     return Markdown::advance(pos);
 }
 
-md_charpos Markdown::advance_up_to_plainish_only(md_charpos pos, md_charpos end) {
+md_charpos Markdown::advance_up_to_plainish_only(md_charpos pos, md_charpos end) {
     if ((Markdown::somewhere(end)) &&
         (pos.md->sliced_from == end.md->sliced_from) && (pos.at >= end.at))
         return Markdown::nowhere();
     return Markdown::advance_plainish_only(pos);
 }
 
-md_charpos Markdown::advance_up_to_quasi_plainish_only(md_charpos pos, md_charpos end) {
+md_charpos Markdown::advance_up_to_quasi_plainish_only(md_charpos pos, md_charpos end) {
     if ((Markdown::somewhere(end)) &&
         (pos.md->sliced_from == end.md->sliced_from) && (pos.at >= end.at))
         return Markdown::nowhere();
@@ -1063,11 +1064,11 @@ 

-wchar_t Markdown::get(md_charpos pos) {
+wchar_t Markdown::get(md_charpos pos) {
     return Markdown::get_offset(pos, 0);
 }
 
-wchar_t Markdown::get_offset(md_charpos pos, int by) {
+wchar_t Markdown::get_offset(md_charpos pos, int by) {
     if (Markdown::somewhere(pos)) return Markdown::get_at(pos.md, pos.at + by);
     return 0;
 }
@@ -1076,7 +1077,7 @@ 

Markdown::put_offset(pos, 0, c); } -void Markdown::put_offset(md_charpos pos, int by, wchar_t c) { +void Markdown::put_offset(md_charpos pos, int by, wchar_t c) { if (Markdown::somewhere(pos)) Str::put_at(pos.md->sliced_from, pos.at + by, c); }

@@ -1091,7 +1092,7 @@

-void Markdown::cut_to_just_before(markdown_item *chain_from, md_charpos cut_point,
+void Markdown::cut_to_just_before(markdown_item *chain_from, md_charpos cut_point,
     markdown_item **left_segment, markdown_item **right_segment) {
     markdown_item *L = chain_from, *R = NULL;
     if ((chain_from) && (Markdown::somewhere(cut_point))) {
@@ -1122,7 +1123,7 @@ 

-void Markdown::cut_to_just_at(markdown_item *chain_from, md_charpos cut_point,
+void Markdown::cut_to_just_at(markdown_item *chain_from, md_charpos cut_point,
     markdown_item **left_segment, markdown_item **right_segment) {
     markdown_item *L = chain_from, *R = NULL;
     if ((chain_from) && (Markdown::somewhere(cut_point))) {
@@ -1153,7 +1154,7 @@ 

-void Markdown::cut_interval(markdown_item *chain_from, md_charpos A, md_charpos B,
+void Markdown::cut_interval(markdown_item *chain_from, md_charpos A, md_charpos B,
     markdown_item **left_segment, markdown_item **middle_segment, markdown_item **right_segment) {
     markdown_item *interstitial = NULL;
     Markdown::cut_to_just_before(chain_from, A, left_segment, &interstitial);
@@ -1178,7 +1179,7 @@ 

CLASS_DEFINITION } md_link_dictionary_entry; -md_links_dictionary *Markdown::new_links_dictionary(void) { +md_links_dictionary *Markdown::new_links_dictionary(void) { md_links_dictionary *dict = CREATE(md_links_dictionary); dict->dict = Dictionaries::new(32, FALSE); of md_link_dictionary_entry return dict; @@ -1193,7 +1194,7 @@

-void Markdown::create(md_links_dictionary *dict, text_stream *label,
+void Markdown::create(md_links_dictionary *dict, text_stream *label,
     text_stream *destination, text_stream *title) {
     if (dict) {
         Markdown::normalise_link_label(label);
@@ -1217,7 +1218,7 @@ 

-md_link_dictionary_entry *Markdown::look_up(md_links_dictionary *dict, text_stream *label) {
+md_link_dictionary_entry *Markdown::look_up(md_links_dictionary *dict, text_stream *label) {
     if (dict == NULL) return NULL;
     if (Str::is_whitespace(label)) return NULL;
     if (Str::len(label) > 999) return NULL;
@@ -1240,7 +1241,7 @@ 

-void Markdown::normalise_link_label(text_stream *label) {
+void Markdown::normalise_link_label(text_stream *label) {
     TEMPORARY_TEXT(normal)
     for (int i=0, ws = FALSE; i<Str::len(label); i++) {
         wchar_t c = Str::get_at(label, i);
@@ -1263,7 +1264,7 @@ 

-void Markdown::debug_char(OUTPUT_STREAM, wchar_t c) {
+void Markdown::debug_char(OUTPUT_STREAM, wchar_t c) {
     switch (c) {
         case 0:    WRITE("NULL"); break;
         case '\n': WRITE("NEWLINE"); break;
@@ -1274,7 +1275,7 @@ 

} } -void Markdown::debug_char_briefly(OUTPUT_STREAM, wchar_t c) { +void Markdown::debug_char_briefly(OUTPUT_STREAM, wchar_t c) { switch (c) { case 0: WRITE("\\x0000"); break; case '\n': WRITE("\\n"); break; @@ -1284,7 +1285,7 @@

} } -void Markdown::debug_pos(OUTPUT_STREAM, md_charpos A) { +void Markdown::debug_pos(OUTPUT_STREAM, md_charpos A) { if (Markdown::somewhere(A) == FALSE) { WRITE("{nowhere}"); return; } WRITE("{"); Markdown::debug_item(OUT, A.md); @@ -1293,7 +1294,7 @@

WRITE("}"); } -void Markdown::debug_interval(OUTPUT_STREAM, md_charpos A, md_charpos B) { +void Markdown::debug_interval(OUTPUT_STREAM, md_charpos A, md_charpos B) { if (Markdown::somewhere(A) == FALSE) { WRITE("NONE\n"); return; } WRITE("["); Markdown::debug_pos(OUT, A); @@ -1308,7 +1309,7 @@

WRITE("]\n"); } -void Markdown::debug_item(OUTPUT_STREAM, markdown_item *md) { +void Markdown::debug_item(OUTPUT_STREAM, markdown_item *md) { if (md == NULL) { WRITE("<no-item>"); return; } if (md->open == TRUE) WRITE("*"); if (md->open == FALSE) WRITE("."); @@ -1353,7 +1354,7 @@

 int md_db_cycle_count = 1;
 
-void Markdown::debug_subtree(OUTPUT_STREAM, markdown_item *md) {
+void Markdown::debug_subtree(OUTPUT_STREAM, markdown_item *md) {
     md_db_cycle_count++;
     Markdown::debug_item_r(OUT, md);
 }
@@ -1362,7 +1363,7 @@ 

Markdown::debug_chain_label(OUT, md, I"CHAIN"); } -void Markdown::debug_chain_label(OUTPUT_STREAM, markdown_item *md, text_stream *label) { +void Markdown::debug_chain_label(OUTPUT_STREAM, markdown_item *md, text_stream *label) { md_db_cycle_count++; WRITE("%S:\n", label); INDENT; @@ -1380,7 +1381,7 @@

-void Markdown::debug_item_r(OUTPUT_STREAM, markdown_item *md) {
+void Markdown::debug_item_r(OUTPUT_STREAM, markdown_item *md) {
     if (md) {
         Markdown::debug_item(OUT, md);
         if (md->cycle_count == md_db_cycle_count) {
diff --git a/docs/foundation-module/9-pl.html b/docs/foundation-module/9-pl.html
index f1efd23d..c6081b1e 100644
--- a/docs/foundation-module/9-pl.html
+++ b/docs/foundation-module/9-pl.html
@@ -64,7 +64,7 @@ 

default_programming_language_path = P; } -programming_language *Languages::find_by_name(text_stream *lname, pathname *P, +programming_language *Languages::find_by_name(text_stream *lname, pathname *P, int error_if_not_found) { programming_language *pl; If this is the name of a language already known, return that2.1; diff --git a/docs/foundation-module/9-tp.html b/docs/foundation-module/9-tp.html index eb4fd457..dcb5e4a6 100644 --- a/docs/foundation-module/9-tp.html +++ b/docs/foundation-module/9-tp.html @@ -67,7 +67,7 @@

 int colouring_state = PLAIN_COLOUR;
 int painter_count = 1;
-void Painter::reset_syntax_colouring(programming_language *pl) {
+void Painter::reset_syntax_colouring(programming_language *pl) {
     colouring_state = PLAIN_COLOUR;
     painter_count = 1;
 }
@@ -93,7 +93,7 @@ 

-int Painter::syntax_colour(programming_language *pl,
+int Painter::syntax_colour(programming_language *pl,
     hash_table *HT, text_stream *matter, text_stream *colouring, int with_comments) {
     int from = 0, to = Str::len(matter) - 1;
     if (with_comments) {
diff --git a/docs/goldbach/goldbach.pdf b/docs/goldbach/goldbach.pdf
index 5a9195df1876d258e33f1861e345ac66663ac392..f1170955f3f9711c1334c2dcf0337a041ff448af 100644
GIT binary patch
delta 137
zcmcb&f&bnH{)QID7N#xC0w*<%jE&7r&9n^+)eQ{PHM#VC^HW?BOHvgyT&#=?jEoH|
z4a^`)wp*TLUdQX~YG`igVq|P;Ze-?UXzXm_=xS_Y=wxK#Xzpt4 =
 	markdown_feature *pd = MarkdownVariations::new_feature(I"phrase defn boxes",
 		PHRASE_DEFN_BOXES_MARKDOWNFEATURE);
+	METHOD_ADD(pd, POST_PHASE_I_MARKDOWN_MTID,
+		InformFlavouredMarkdown::PD_intervene_after_Phase_I);
 	METHOD_ADD(pd, RENDER_MARKDOWN_MTID, InformFlavouredMarkdown::PD_render);
 	MarkdownVariations::add_feature(Inform_flavoured_Markdown, PHRASE_DEFN_BOXES_MARKDOWNFEATURE);
+	Markdown::new_leaf_block_type(PHRASE_HEADER_MIT, I"PHRASE_HEADER");
 
 @ =
-int InformFlavouredMarkdown::PD_render(markdown_feature *feature, text_stream *OUT,
-	markdown_item *md, int mode) {
+void InformFlavouredMarkdown::PD_intervene_after_Phase_I(markdown_feature *feature,
+	markdown_item *md, md_links_dictionary *link_references) {
 	if (md->type == BLOCK_QUOTE_MIT) {
 		if ((md->down) && (md->down->type == PARAGRAPH_MIT)) {
 			match_results mr = Regexp::create_mr();
-			if ((Regexp::match(&mr, md->down->stashed, L"phrase: *{(%c*?)} *(%c+?)\n(%c*)")) ||
-				(Regexp::match(&mr, md->down->stashed, L"(phrase): *(%c+?)\n(%c*)"))) {
-				HTML_OPEN_WITH("div", "class=\"definition\"");
-				HTML_OPEN_WITH("p", "class=\"defnprototype\"");
-				WRITE("%S", mr.exp[1]);
-				HTML_CLOSE("p");
-				HTML_TAG("br");
-				markdown_item *remainder =
-					Markdown::parse_inline_extended(mr.exp[2], InformFlavouredMarkdown::variation());
-				Markdown::render_extended(OUT, remainder, InformFlavouredMarkdown::variation());
-				for (markdown_item *ch = md->down->next; ch; ch = ch->next)
-					Markdown::render_extended(OUT, ch, InformFlavouredMarkdown::variation());
-				HTML_CLOSE("div");
-				Regexp::dispose_of(&mr);
-				return TRUE;
+			if ((Regexp::match(&mr, md->down->stashed, L"phrase: *%{(%c*?)%} *(%c+?)")) ||
+				(Regexp::match(&mr, md->down->stashed, L"(phrase): *(%c+?)"))) {
+				markdown_item *join_to = NULL;
+				TEMPORARY_TEXT(phrase)
+				for (int i=0; i;
+						Str::clear(phrase);
+					} else {
+						PUT_TO(phrase, Str::get_at(mr.exp[1], i));
+					}
+				}
+				@;
+				DISCARD_TEXT(phrase)
 			}
-			Regexp::dispose_of(&mr);
 		}
 	}
+	for (markdown_item *ch = md->down; ch; ch=ch->next) {
+		InformFlavouredMarkdown::PD_intervene_after_Phase_I(feature, ch, link_references);
+	}
+}
+
+@ =
+	Str::trim_white_space(phrase);
+	if (Str::len(phrase) > 0) {
+		markdown_item *ph = Markdown::new_item(PHRASE_HEADER_MIT);
+		ph->stashed = Str::duplicate(phrase);
+		if (join_to == NULL) { ph->next = md->down->next; md->down = ph; }
+		else { ph->next = join_to->next; join_to->next = ph; }
+		join_to = ph; 
+
+		markdown_item *im = Markdown::new_item(INDEX_MARKER_MIT);
+		im->stashed = Str::new();
+		text_stream *category = I"+to+";
+		if (Str::begins_with(phrase, I"if ")) category = I"+toif+";
+		if (Str::begins_with(phrase, I"say ")) category = I"+tosay+";
+		WRITE_TO(im->stashed, "%S%S", category, phrase);
+		if (Str::get_last_char(im->stashed) == ':')
+			Str::delete_last_character(im->stashed);
+		im->details = 1;
+		im->next = join_to->next; join_to->next = im;
+		join_to = im; 
+	}
+
+@
+
+=
+int InformFlavouredMarkdown::PD_render(markdown_feature *feature, text_stream *OUT,
+	markdown_item *md, int mode) {
+	if ((md->type == BLOCK_QUOTE_MIT) && (md->down) && (md->down->type == PHRASE_HEADER_MIT)) {
+		HTML_OPEN_WITH("div", "class=\"definition\"");
+		for (markdown_item *ch = md->down; ch; ch = ch->next)
+			Markdown::render_extended(OUT, ch, InformFlavouredMarkdown::variation());
+		HTML_CLOSE("div");
+		return TRUE;
+	}
+	if (md->type == PHRASE_HEADER_MIT) {
+		HTML_OPEN_WITH("p", "class=\"defnprototype\"");
+		MDRenderer::stream(OUT, md->stashed, mode);
+		HTML_CLOSE("p");
+		return TRUE;
+	}
 	return FALSE;
 }
 
diff --git a/foundation-module/Chapter 5/Markdown Phase II.w b/foundation-module/Chapter 5/Markdown Phase II.w
index 916a1e0e..78de9c06 100644
--- a/foundation-module/Chapter 5/Markdown Phase II.w	
+++ b/foundation-module/Chapter 5/Markdown Phase II.w	
@@ -197,6 +197,7 @@ notation, also used by indoc.
 
 @ =
 	int name_inversion = FALSE;
+
 	if (Str::get_first_char(lemma) == '@') {
 		Str::delete_first_character(lemma);
 		name_inversion = TRUE;
diff --git a/foundation-module/Chapter 5/Markdown Rendering.w b/foundation-module/Chapter 5/Markdown Rendering.w
index 16b8bdb2..05ec1202 100644
--- a/foundation-module/Chapter 5/Markdown Rendering.w	
+++ b/foundation-module/Chapter 5/Markdown Rendering.w	
@@ -423,6 +423,14 @@ void MDRenderer::slice(OUTPUT_STREAM, markdown_item *md, int mode) {
 	}
 }
 
+@ Alternatively:
+
+=
+void MDRenderer::stream(OUTPUT_STREAM, text_stream *stream, int mode) {
+	for (int i=0; ifrom = md->from;
 	copied->to = md->to;
+	copied->stashed = md->stashed;
 	copied->copied_from = md;
 	for (markdown_item *c = md->down; c; c = c->next)
 		Markdown::add_to(Markdown::deep_copy(c), copied);