diff --git a/README.md b/README.md index f8eb890cc..acab04d98 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6X19 'Krypton' (20 September 2023) +[Version](notes/versioning.md): 10.2.0-beta+6X20 'Krypton' (21 September 2023) ## About Inform diff --git a/build.txt b/build.txt index 0cfcb70c6..9ff340cf2 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 20 September 2023 -Build Number: 6X19 +Build Date: 21 September 2023 +Build Number: 6X20 diff --git a/docs/supervisor-module/1-sm.html b/docs/supervisor-module/1-sm.html index 53b739683..94b08a8be 100644 --- a/docs/supervisor-module/1-sm.html +++ b/docs/supervisor-module/1-sm.html @@ -73,6 +73,7 @@ which use this module: enum build_skill_CLASS enum build_step_CLASS enum build_vertex_CLASS +enum cd_image_CLASS enum cd_layout_error_CLASS enum cd_pageset_CLASS enum cd_source_file_CLASS @@ -114,6 +115,7 @@ which use this module: DECLARE_CLASS(build_skill) DECLARE_CLASS(build_step) DECLARE_CLASS(build_vertex) +DECLARE_CLASS(cd_image) DECLARE_CLASS(cd_layout_error) DECLARE_CLASS(cd_pageset) DECLARE_CLASS(cd_source_file) diff --git a/docs/supervisor-module/4-ebm.html b/docs/supervisor-module/4-ebm.html index 9e6da302e..8ae44d36a 100644 --- a/docs/supervisor-module/4-ebm.html +++ b/docs/supervisor-module/4-ebm.html @@ -413,6 +413,8 @@ copy name. ; } else if (Str::eq(subdir, I"Tests")) { ; + } else if (Str::eq(subdir, I"Images")) { + ; } else { TEMPORARY_TEXT(error_text) WRITE_TO(error_text, diff --git a/docs/supervisor-module/7-dc.html b/docs/supervisor-module/7-dc.html index 648fe479e..dcc6a2039 100644 --- a/docs/supervisor-module/7-dc.html +++ b/docs/supervisor-module/7-dc.html @@ -86,11 +86,11 @@ torn-off documentation is found: compiled_documentation *DocumentationCompiler::compile_from_text(text_stream *scrap, inform_extension *associated_extension, filename *sitemap) { SVEXPLAIN(1, "(compiling documentation: %d chars)\n", Str::len(scrap)); - compiled_documentation *cd = DocumentationCompiler::new_cd(NULL, associated_extension, sitemap); + compiled_documentation *cd = DocumentationCompiler::new_cd(NULL, associated_extension, sitemap); cd_volume *vol = FIRST_IN_LINKED_LIST(cd_volume, cd->volumes); cd_pageset *page = FIRST_IN_LINKED_LIST(cd_pageset, vol->pagesets); page->nonfile_content = scrap; - DocumentationCompiler::compile_inner(cd); + DocumentationCompiler::compile_inner(cd); return cd; } @@ -102,8 +102,8 @@ the documentation for a directory-format extension.
 compiled_documentation *DocumentationCompiler::compile_from_path(pathname *P,
     inform_extension *associated_extension, filename *sitemap) {
-    compiled_documentation *cd = DocumentationCompiler::new_cd(P, associated_extension, sitemap);
-    DocumentationCompiler::compile_inner(cd);
+    compiled_documentation *cd = DocumentationCompiler::new_cd(P, associated_extension, sitemap);
+    DocumentationCompiler::compile_inner(cd);
     return cd;
 }
 
@@ -126,6 +126,8 @@ the documentation for a directory-format extension. struct pathname *domain; where the documentation source is struct linked_list *source_files; of cd_source_file struct linked_list *layout_errors; of cd_layout_error + struct linked_list *images; of cd_image + struct text_stream *images_URL; struct linked_list *volumes; of cd_volume struct text_stream *contents_URL_pattern; @@ -163,7 +165,7 @@ read to compile the volumes of documentation. CLASS_DEFINITION } cd_source_file; - +

§6. A cd contains one or more "volumes". For something simple like an extension, there will usually just be one volume, with the same title as the whole cd. For the Inform manual built in to the apps, there will be two volumes, @@ -181,7 +183,7 @@ For the Inform manual built in to the apps, there will be two volumes, CLASS_DEFINITION } cd_volume; -cd_volume *DocumentationCompiler::add_volume(compiled_documentation *cd, text_stream *title, +cd_volume *DocumentationCompiler::add_volume(compiled_documentation *cd, text_stream *title, text_stream *label, text_stream *home_URL) { cd_volume *vol = CREATE(cd_volume); vol->title = Str::duplicate(title); @@ -194,7 +196,7 @@ For the Inform manual built in to the apps, there will be two volumes, return vol; } -cd_volume *DocumentationCompiler::find_volume(compiled_documentation *cd, text_stream *title) { +cd_volume *DocumentationCompiler::find_volume(compiled_documentation *cd, text_stream *title) { cd_volume *V; LOOP_OVER_LINKED_LIST(V, cd_volume, cd->volumes) if ((Str::eq_insensitive(title, V->title)) || (Str::eq_insensitive(title, V->label))) @@ -237,7 +239,7 @@ chapter or section structure: CLASS_DEFINITION } cd_pageset; -cd_pageset *DocumentationCompiler::add_page(cd_volume *vol, text_stream *src, text_stream *dest, +cd_pageset *DocumentationCompiler::add_page(cd_volume *vol, text_stream *src, text_stream *dest, int breaking) { cd_pageset *pages = CREATE(cd_pageset); pages->source_specification = Str::duplicate(src); @@ -261,7 +263,7 @@ syntax errors or asks for something ambiguous or impossible: CLASS_DEFINITION } cd_layout_error; -void DocumentationCompiler::layout_error(compiled_documentation *cd, +void DocumentationCompiler::layout_error(compiled_documentation *cd, text_stream *msg, text_stream *line, text_file_position *tfp) { cd_layout_error *err = CREATE(cd_layout_error); err->message = Str::duplicate(msg); @@ -319,18 +321,71 @@ index page and otherwise not producting documentation at all: return TRUE; } -

§10. And we can now create a new cd object. +

§10. "Images" are image files, that is, pictures.

-compiled_documentation *DocumentationCompiler::new_cd(pathname *P,
+typedef struct cd_image {
+    struct filename *source;
+    struct text_stream *final_leafname;
+    struct text_stream *prefix;
+    struct text_stream *correct_URL;
+    int used;
+    CLASS_DEFINITION
+} cd_image;
+
+void DocumentationCompiler::add_images(compiled_documentation *cd, pathname *figures,
+    text_stream *prefix) {
+    linked_list *L = Directories::listing(figures);
+    text_stream *entry;
+    LOOP_OVER_LINKED_LIST(entry, text_stream, L) {
+        if (Platform::is_folder_separator(Str::get_last_char(entry)) == FALSE) {
+            cd_image *cdim = CREATE(cd_image);
+            cdim->source = Filenames::in(figures, entry);
+            LOOP_THROUGH_TEXT(pos, entry)
+                Str::put(pos, Characters::tolower(Str::get(pos)));
+            cdim->final_leafname = Str::duplicate(entry);
+            cdim->correct_URL = Str::new();
+            if (Str::len(prefix) > 0) {
+                WRITE_TO(cdim->correct_URL, "%S/%S", prefix, entry);
+            } else {
+                WRITE_TO(cdim->correct_URL, "%S", entry);
+            }
+            cdim->prefix = Str::duplicate(prefix);
+            cdim->used = FALSE;
+            ADD_TO_LINKED_LIST(cdim, cd_image, cd->images);
+            Markdown::create(cd->link_references, Str::duplicate(entry), cdim->correct_URL, NULL);
+        }
+    }
+}
+
+compiled_documentation *cd_being_watched_for_image_use = NULL;
+void DocumentationCompiler::watch_image_use(compiled_documentation *cd) {
+    cd_being_watched_for_image_use = cd;
+}
+void DocumentationCompiler::notify_image_use(text_stream *URL) {
+    if (cd_being_watched_for_image_use) {
+        cd_image *cdim;
+        LOOP_OVER_LINKED_LIST(cdim, cd_image, cd_being_watched_for_image_use->images)
+            if (Str::eq(URL, cdim->correct_URL))
+                cdim->used = TRUE;
+    }
+}
+
+ +

§11. And we can now create a new cd object. +

+ +
+compiled_documentation *DocumentationCompiler::new_cd(pathname *P,
     inform_extension *associated_extension, filename *sitemap) {
     compiled_documentation *cd = CREATE(compiled_documentation);
-    Initialise the cd structure10.1;
+    Initialise the cd structure11.1;
     if (P) {
         cd_source_file *Documentation_md_cdsf = NULL;
-        Find the possible Markdown source files10.2;
-        Read the contents and sitemap files, if they exist10.3;
+        Find the possible Markdown source files11.2;
+        Read the contents and sitemap files, if they exist11.3;
+        DocumentationCompiler::add_images(cd, Pathnames::down(P, I"Images"), cd->images_URL);
     }
     Indexes::add_indexing_notation(cd, NULL, NULL, I"standard", NULL);
     Indexes::add_indexing_notation(cd, I"@", NULL, I"name", I"(invert)");
@@ -355,7 +410,7 @@ index page and otherwise not producting documentation at all:
     return cd;
 }
 
-

§10.1. Initialise the cd structure10.1 = +

§11.1. Initialise the cd structure11.1 =

@@ -391,9 +446,11 @@ index page and otherwise not producting documentation at all:
     cd->duplex_contents_page = FALSE;
     cd->source_files = NEW_LINKED_LIST(cd_source_file);
     cd->domain = P;
+    cd->images = NEW_LINKED_LIST(cd_image);
+    cd->images_URL = I"images";
 
- -

§10.2. Find the possible Markdown source files10.2 = +

+

§11.2. Find the possible Markdown source files11.2 =

@@ -413,8 +470,8 @@ index page and otherwise not producting documentation at all:
         }
     }
 
- -

§10.3. Read the contents and sitemap files, if they exist10.3 = +

+

§11.3. Read the contents and sitemap files, if they exist11.3 =

@@ -422,17 +479,17 @@ index page and otherwise not producting documentation at all:
     if (sitemap == NULL) sitemap = Filenames::in(P, I"sitemap.txt");
     if (TextFiles::exists(layout_file))
         TextFiles::read(layout_file, FALSE, "can't open layout file",
-            TRUE, DocumentationCompiler::read_contents_helper, NULL, cd);
+            TRUE, DocumentationCompiler::read_contents_helper, NULL, cd);
     else if (Documentation_md_cdsf) Documentation_md_cdsf->used = TRUE;
     if (TextFiles::exists(sitemap))
         TextFiles::read(sitemap, FALSE, "can't open sitemap file",
-            TRUE, DocumentationCompiler::read_sitemap_helper, NULL, cd);
+            TRUE, DocumentationCompiler::read_sitemap_helper, NULL, cd);
 
- -

§11.

+ +

§12.

-void DocumentationCompiler::read_contents_helper(text_stream *cl, text_file_position *tfp,
+void DocumentationCompiler::read_contents_helper(text_stream *cl, text_file_position *tfp,
     void *v_cd) {
     compiled_documentation *cd = (compiled_documentation *) v_cd;
     Str::trim_white_space(cl);
@@ -441,25 +498,25 @@ index page and otherwise not producting documentation at all:
     if (Regexp::match(&mr, cl, U" *")) { Regexp::dispose_of(&mr); return; }
 
     if (Regexp::match(&mr, cl, U" *text: *(%c*?)")) {
-        Act on a text declaration11.2;
+        Act on a text declaration12.2;
     } else if (Regexp::match(&mr, cl, U" *volume: *\"(%c*?)\" or \"(%C+)\"")) {
-        Act on a volume declaration11.1;
+        Act on a volume declaration12.1;
     } else if (Regexp::match(&mr, cl, U" *index notation: *(%c*?)")) {
-        Act on an indexing notation11.3;
+        Act on an indexing notation12.3;
     } else {
         DocumentationCompiler::layout_error(cd, I"unknown syntax in content.txt file", cl, tfp);
     }
     Regexp::dispose_of(&mr);
 }
 
-

§11.1. Act on a volume declaration11.1 = +

§12.1. Act on a volume declaration12.1 =

     DocumentationCompiler::add_volume(cd, mr.exp[0], mr.exp[1], NULL);
 
- -

§11.2. Act on a text declaration11.2 = +

+

§12.2. Act on a text declaration12.2 =

@@ -522,8 +579,8 @@ index page and otherwise not producting documentation at all:
     }
     DISCARD_TEXT(src)
 
- -

§11.3. Act on an indexing notation11.3 = +

+

§12.3. Act on an indexing notation12.3 =

@@ -544,11 +601,11 @@ index page and otherwise not producting documentation at all:
     }
     Regexp::dispose_of(&mr2);
 
- -

§12.

+ +

§13.

-void DocumentationCompiler::read_sitemap_helper(text_stream *cl, text_file_position *tfp,
+void DocumentationCompiler::read_sitemap_helper(text_stream *cl, text_file_position *tfp,
     void *v_cd) {
     compiled_documentation *cd = (compiled_documentation *) v_cd;
     Str::trim_white_space(cl);
@@ -558,6 +615,8 @@ index page and otherwise not producting documentation at all:
 
     if (Regexp::match(&mr, cl, U" *cross-references: to \"(%c*)\"")) {
         cd->xrefs_file_pattern = Str::duplicate(mr.exp[0]);
+    } else if (Regexp::match(&mr, cl, U" *images: to \"(%c*)\"")) {
+        cd->images_URL = Str::duplicate(mr.exp[0]);
     } else if (Regexp::match(&mr, cl, U" *contents: *(%c+?) to \"(%c*)\"")) {
         if (Str::eq(mr.exp[0], I"standard")) cd->duplex_contents_page = FALSE;
         else if (Str::eq(mr.exp[0], I"duplex")) cd->duplex_contents_page = TRUE;
@@ -569,9 +628,9 @@ index page and otherwise not producting documentation at all:
         else DocumentationCompiler::layout_error(cd, I"'examples:' must be 'lettered' or 'numbered'", cl, tfp);
         cd->example_URL_pattern = Str::duplicate(mr.exp[1]);
     } else if (Regexp::match(&mr, cl, U" *pages: *(%c*?) *")) {
-        Act on a page-set declaration12.2;
+        Act on a page-set declaration13.2;
     } else if (Regexp::match(&mr, cl, U" *volume contents: *\"(%c*?)\" to \"(%c*?)\"")) {
-        Act on a volume contents declaration12.1;
+        Act on a volume contents declaration13.1;
     } else if (Regexp::match(&mr, cl, U" *alphabetical index: *\"(%c*?)\" to \"(%c*?)\" *")) {
         cd->index_title[ALPHABETICAL_EG_INDEX] = Str::duplicate(mr.exp[0]);
         cd->index_URL_pattern[ALPHABETICAL_EG_INDEX] = Str::duplicate(mr.exp[1]);
@@ -594,7 +653,7 @@ index page and otherwise not producting documentation at all:
     Regexp::dispose_of(&mr);
 }
 
-

§12.1. Act on a volume contents declaration12.1 = +

§13.1. Act on a volume contents declaration13.1 =

@@ -604,8 +663,8 @@ index page and otherwise not producting documentation at all:
     if (V) V->home_URL = Str::duplicate(mr.exp[1]);
     else DocumentationCompiler::layout_error(cd, I"unknown volume in sitemap file", cl, tfp);
 
- -

§12.2. Act on a page-set declaration12.2 = +

+

§13.2. Act on a page-set declaration13.2 =

@@ -685,8 +744,8 @@ index page and otherwise not producting documentation at all:
     }
     DISCARD_TEXT(dest)
 
- -

§13. "Satellite test cases" is an umbrella term including both examples and test +

+

§14. "Satellite test cases" is an umbrella term including both examples and test cases, all of which are tested when an extension (say) is tested.

@@ -707,7 +766,7 @@ cases, all of which are tested when an extension (say) is tested. CLASS_DEFINITION } satellite_test_case; -satellite_test_case *DocumentationCompiler::new_satellite(compiled_documentation *cd, +satellite_test_case *DocumentationCompiler::new_satellite(compiled_documentation *cd, int is_eg, text_stream *short_name, filename *F) { satellite_test_case *stc = CREATE(satellite_test_case); stc->is_example = is_eg; @@ -732,24 +791,24 @@ cases, all of which are tested when an extension (say) is tested. } -

§14. Satellites for a cd consist of examples in the Examples subdirectory and +

§15. Satellites for a cd consist of examples in the Examples subdirectory and tests in the Tests one.

-int DocumentationCompiler::detect_satellites(compiled_documentation *cd) {
+int DocumentationCompiler::detect_satellites(compiled_documentation *cd) {
     if (cd->domain) {
         pathname *EP = Pathnames::down(cd->domain, I"Examples");
         int egs = TRUE;
-        Scan EP directory for examples14.1;
+        Scan EP directory for examples15.1;
         egs = FALSE;
         EP = Pathnames::down(cd->domain, I"Tests");
-        Scan EP directory for examples14.1;
+        Scan EP directory for examples15.1;
     }
     return LinkedLists::len(cd->cases);
 }
 
-

§14.1. Scan EP directory for examples14.1 = +

§15.1. Scan EP directory for examples15.1 =

@@ -769,16 +828,16 @@ tests in the Tests                    || (Str::get_at(short_name, Str::len(short_name)-1) == 'i')))
                 continue;
             satellite_test_case *stc =
-                DocumentationCompiler::new_satellite(cd, egs, short_name, F);
+                DocumentationCompiler::new_satellite(cd, egs, short_name, F);
             if (stc->is_example)
-                Scan the example for its header and content14.1.2;
+                Scan the example for its header and content15.1.2;
         }
         DISCARD_TEXT(leafname)
         Directories::close(D);
     }
 
- -

§14.1.1. Scanning the examples is not a trivial process, because it involves going +

+

§15.1.1. Scanning the examples is not a trivial process, because it involves going through the metadata and also capturing the Markdown material.

@@ -798,7 +857,7 @@ through the metadata and also capturing the Markdown material. } example_scanning_state; -

§14.1.2. Scan the example for its header and content14.1.2 = +

§15.1.2. Scan the example for its header and content15.1.2 =

@@ -816,7 +875,7 @@ through the metadata and also capturing the Markdown material.
     ess.scanning = Str::new();
     WRITE_TO(ess.scanning, "%S", Filenames::get_leafname(stc->test_file));
     TextFiles::read(stc->test_file, FALSE, "unable to read file of example", TRUE,
-        &DocumentationCompiler::read_example_helper, NULL, &ess);
+        &DocumentationCompiler::read_example_helper, NULL, &ess);
 
     cd_volume *primary = NULL;
     cd_volume *secondary = NULL;
@@ -831,7 +890,7 @@ through the metadata and also capturing the Markdown material.
         if (stc->primary_placement == NULL) {
             text_stream *err = Str::new();
             WRITE_TO(err, "example gives Location '%S', which is not the name of any section", ess.placement);
-            DocumentationCompiler::example_error(&ess, err);
+            DocumentationCompiler::example_error(&ess, err);
         }
     }
 
@@ -841,12 +900,12 @@ through the metadata and also capturing the Markdown material.
         if (stc->secondary_placement == NULL) {
             text_stream *err = Str::new();
             WRITE_TO(err, "example gives RecipeLocation '%S', which is not the name of any section", ess.recipe_placement);
-            DocumentationCompiler::example_error(&ess, err);
+            DocumentationCompiler::example_error(&ess, err);
         }
     }
 
     if (Str::len(ess.desc) == 0) {
-        DocumentationCompiler::example_error(&ess,
+        DocumentationCompiler::example_error(&ess,
             I"example does not give its Description");
     }
     IFM_example *eg = InformFlavouredMarkdown::new_example(
@@ -863,21 +922,21 @@ through the metadata and also capturing the Markdown material.
     stc->visible_documentation = Str::duplicate(ess.body_text);
     stc->example_errors = ess.errors;
 
- -

§15.

+ +

§16.

-void DocumentationCompiler::example_error(example_scanning_state *ess, text_stream *text) {
+void DocumentationCompiler::example_error(example_scanning_state *ess, text_stream *text) {
     text_stream *err = Str::new();
     WRITE_TO(err, "Example file '%S': %S", ess->scanning, text);
     markdown_item *E = InformFlavouredMarkdown::error_item(err);
     ADD_TO_LINKED_LIST(E, markdown_item, ess->errors);
 }
 
-

§16.

+

§17.

-void DocumentationCompiler::read_example_helper(text_stream *text, text_file_position *tfp,
+void DocumentationCompiler::read_example_helper(text_stream *text, text_file_position *tfp,
     void *v_state) {
     example_scanning_state *ess = (example_scanning_state *) v_state;
     if (tfp->line_count == 1) {
@@ -886,18 +945,18 @@ through the metadata and also capturing the Markdown material.
             (Regexp::match(&mr, text, U"Example *- *(%**) *(%c+?)"))) {
             ess->star_count = Str::len(mr.exp[0]);
             if (ess->star_count == 0) {
-                DocumentationCompiler::example_error(ess,
+                DocumentationCompiler::example_error(ess,
                     I"this example should be marked (before the title) '*', '**', '***' or '****' for difficulty");
                 ess->star_count = 1;
             }
             if (ess->star_count > 4) {
-                DocumentationCompiler::example_error(ess,
+                DocumentationCompiler::example_error(ess,
                     I"four stars '****' is the maximum difficulty rating allowed");
                 ess->star_count = 4;
             }
             ess->long_title = Str::duplicate(mr.exp[1]);
         } else {
-            DocumentationCompiler::example_error(ess,
+            DocumentationCompiler::example_error(ess,
                 I"titling line of example file is malformed");
         }
         Regexp::dispose_of(&mr);
@@ -911,7 +970,7 @@ through the metadata and also capturing the Markdown material.
             else if (Str::eq(mr.exp[0], I"Index")) ess->index = Str::duplicate(mr.exp[1]);
             else if (Str::eq(mr.exp[0], I"Description")) ess->desc = Str::duplicate(mr.exp[1]);
         } else {
-            DocumentationCompiler::example_error(ess,
+            DocumentationCompiler::example_error(ess,
                 I"header line of example file is malformed");
         }
         Regexp::dispose_of(&mr);
@@ -920,14 +979,14 @@ through the metadata and also capturing the Markdown material.
     }
 }
 
-

§17. Stage two of sorting out the satellites is to put special items into the +

§18. Stage two of sorting out the satellites is to put special items into the Markdown tree for the cd which mark the places where the examples are referred to. Note that an example must appear in the primary volume, and can also appear in the secondary (if there is one).

-void DocumentationCompiler::place_example_heading_items(compiled_documentation *cd) {
+void DocumentationCompiler::place_example_heading_items(compiled_documentation *cd) {
     satellite_test_case *stc;
     LOOP_OVER_LINKED_LIST(stc, satellite_test_case, cd->cases) {
         IFM_example *eg = stc->as_example;
@@ -967,7 +1026,7 @@ in the secondary (if there is one).
     }
 }
 
-

§18. And lastly, we can number the examples. This is done as a third stage of +

§19. And lastly, we can number the examples. This is done as a third stage of processing and not as part of the second because we must also pick up example headers explicitly written in the source text of a single-file extension, which do not arise from satellites at all. @@ -978,9 +1037,9 @@ and from that the URL of the HTML file for it.

-void DocumentationCompiler::count_examples(compiled_documentation *cd) {
+void DocumentationCompiler::count_examples(compiled_documentation *cd) {
     int example_number = 0;
-    DocumentationCompiler::recursively_renumber_examples_r(cd->markdown_content,
+    DocumentationCompiler::recursively_renumber_examples_r(cd->markdown_content,
         &example_number, cd->examples_lettered);
 
     IFM_example *eg;
@@ -1013,10 +1072,10 @@ and from that the URL of the HTML file for it.
         }
     }
     for (markdown_item *ch = md->down; ch; ch = ch->next)
-        DocumentationCompiler::recursively_renumber_examples_r(ch, example_number, lettered);
+        DocumentationCompiler::recursively_renumber_examples_r(ch, example_number, lettered);
 }
 
-

§19. We are finally in a position to write DocumentationCompiler::compile_inner, +

§20. We are finally in a position to write DocumentationCompiler::compile_inner, the function which all cd compilations funnel through.

@@ -1027,12 +1086,13 @@ perform Phase II on everything.

-void DocumentationCompiler::compile_inner(compiled_documentation *cd) {
+void DocumentationCompiler::compile_inner(compiled_documentation *cd) {
+    DocumentationCompiler::watch_image_use(cd);
      Phase I parsing
-    DocumentationCompiler::Phase_I_on_volumes(cd);
-    DocumentationCompiler::detect_satellites(cd);
-    DocumentationCompiler::place_example_heading_items(cd);
-    DocumentationCompiler::count_examples(cd);
+    DocumentationCompiler::Phase_I_on_volumes(cd);
+    DocumentationCompiler::detect_satellites(cd);
+    DocumentationCompiler::place_example_heading_items(cd);
+    DocumentationCompiler::count_examples(cd);
     satellite_test_case *stc;
     LOOP_OVER_LINKED_LIST(stc, satellite_test_case, cd->cases) {
         IFM_example *eg = stc->as_example;
@@ -1060,15 +1120,16 @@ perform Phase II on everything.
     if (Indexes::indexing_occurred(cd)) cd->include_index[GENERAL_INDEX] = TRUE;
     if (LinkedLists::len(cd->examples) >= 10) cd->include_index[NUMERICAL_EG_INDEX] = TRUE;
     if (LinkedLists::len(cd->examples) >= 20) cd->include_index[ALPHABETICAL_EG_INDEX] = TRUE;
+    DocumentationCompiler::watch_image_use(NULL);
 }
 
-

§20. In addition to regular Phase I parsing of the content in the volumes, we +

§21. In addition to regular Phase I parsing of the content in the volumes, we want to insert VOLUME_MIT and FILE_MIT items into the tree to mark where new files and volumes begin.

-void DocumentationCompiler::Phase_I_on_volumes(compiled_documentation *cd) {
+void DocumentationCompiler::Phase_I_on_volumes(compiled_documentation *cd) {
     pathname *P = cd->domain;
     cd_volume *vol;
     LOOP_OVER_LINKED_LIST(vol, cd_volume, cd->volumes) {
@@ -1083,7 +1144,7 @@ new files and volumes begin.
                 WRITE_TO(temp, "%S", pages->nonfile_content);
             }
             if (Str::is_whitespace(temp) == FALSE)
-                Content was found for this pageset20.1;
+                Content was found for this pageset21.1;
             DISCARD_TEXT(temp)
         }
     }
@@ -1097,7 +1158,7 @@ new files and volumes begin.
     }
 }
 
-

§20.1. Content was found for this pageset20.1 = +

§21.1. Content was found for this pageset21.1 =

@@ -1107,13 +1168,13 @@ new files and volumes begin.
     if (subtree) {
         switch (pages->breaking) {
             case NO_PAGESETBREAKING:
-                DocumentationCompiler::do_not_divide_tree(subtree, pages->page_specification);
+                DocumentationCompiler::do_not_divide_tree(subtree, pages->page_specification);
                 break;
             case SECTION_PAGESETBREAKING:
-                DocumentationCompiler::divide_tree_by_sections(subtree, pages->page_specification);
+                DocumentationCompiler::divide_tree_by_sections(subtree, pages->page_specification);
                 break;
             case CHAPTER_PAGESETBREAKING:
-                DocumentationCompiler::divide_tree_by_chapters(subtree, pages->page_specification);
+                DocumentationCompiler::divide_tree_by_chapters(subtree, pages->page_specification);
                 break;
         }
         markdown_item *vol_marker = NULL;
@@ -1134,35 +1195,35 @@ new files and volumes begin.
         }
     }
 
- -

§21. The three strategies for breaking up the tree into chapters or sections, +

+

§22. The three strategies for breaking up the tree into chapters or sections, if that's what we were told to do.

-int DocumentationCompiler::do_not_divide_tree(markdown_item *tree, text_stream *naming) {
+int DocumentationCompiler::do_not_divide_tree(markdown_item *tree, text_stream *naming) {
     markdown_item *file_marker = Markdown::new_file_marker(Filenames::from_text(naming));
     file_marker->next = tree->down; tree->down = file_marker;
     return TRUE;
 }
 
-int DocumentationCompiler::divide_tree_by_sections(markdown_item *tree, text_stream *naming) {
+int DocumentationCompiler::divide_tree_by_sections(markdown_item *tree, text_stream *naming) {
     int N = 1, C = 0;
     for (markdown_item *prev_md = NULL, *md = tree->down; md; prev_md = md, md = md->next) {
         if ((md->type == HEADING_MIT) && (Markdown::get_heading_level(md) == 1)) {
             C++; N = 1;
             if ((md->next) && (md->next->type == HEADING_MIT) &&
                 (Markdown::get_heading_level(md->next) == 2)) {
-                Divide by section here21.1;
+                Divide by section here22.1;
                 prev_md = md; md = md->next;
             }
         } else if ((md->type == HEADING_MIT) && (Markdown::get_heading_level(md) == 2))
-            Divide by section here21.1;
+            Divide by section here22.1;
     }
     return TRUE;
 }
 
-

§21.1. Divide by section here21.1 = +

§22.1. Divide by section here22.1 =

@@ -1179,11 +1240,11 @@ if that's what we were told to do.
     if (prev_md) prev_md->next = file_marker; else tree->down = file_marker;
     file_marker->next = md;
 
- -

§22.

+ +

§23.

-int DocumentationCompiler::divide_tree_by_chapters(markdown_item *tree, text_stream *naming) {
+int DocumentationCompiler::divide_tree_by_chapters(markdown_item *tree, text_stream *naming) {
     int N = 1;
     for (markdown_item *prev_md = NULL, *md = tree->down; md; prev_md = md, md = md->next) {
         if ((md->type == HEADING_MIT) && (Markdown::get_heading_level(md) == 1)) {
diff --git a/docs/supervisor-module/7-dr.html b/docs/supervisor-module/7-dr.html
index ed21ee527..d8d70bb10 100644
--- a/docs/supervisor-module/7-dr.html
+++ b/docs/supervisor-module/7-dr.html
@@ -99,6 +99,7 @@ except the examples, and then up to 26 pages holding the content of examples A t
         Languages::set_default_directory(LP);
     }
     if (cd) {
+        DocumentationCompiler::watch_image_use(cd);
         text_stream *OUT = DocumentationRenderer::open_subpage(P, cd->contents_URL_pattern);
         if (OUT) {
             markdown_item *md = NULL;
@@ -214,6 +215,15 @@ except the examples, and then up to 26 pages holding the content of examples A t
                 STREAM_CLOSE(XR);
             }
         }
+        cd_image *cdim;
+        LOOP_OVER_LINKED_LIST(cdim, cd_image, cd->images)
+            if (cdim->used) {
+                pathname *Q = P;
+                if (Str::len(cdim->prefix) > 0) Q = Pathnames::down(Q, cdim->prefix);
+                Pathnames::create_in_file_system(Q);
+                filename *T = Filenames::in(Q, cdim->final_leafname);
+                BinaryFiles::copy(cdim->source, T, TRUE);
+            }
     }
 }
 
diff --git a/docs/supervisor-module/7-gi.html b/docs/supervisor-module/7-gi.html index d71d0e1bd..2c31e63c6 100644 --- a/docs/supervisor-module/7-gi.html +++ b/docs/supervisor-module/7-gi.html @@ -71,17 +71,17 @@ roughly to a CSS class used in the final output.

-void Indexes::add_indexing_notation(compiled_documentation *cd, text_stream *L, text_stream *R, text_stream *style, text_stream *options) {
+void Indexes::add_indexing_notation(compiled_documentation *cd, text_stream *L, text_stream *R, text_stream *style, text_stream *options) {
     IndexUtilities::add_span_notation(cd, L, R, style, INDEX_TEXT_SPP);
     Indexes::add_category(cd, style, options, NULL);
 }
 
-void Indexes::add_indexing_notation_for_symbols(compiled_documentation *cd, text_stream *L, text_stream *style, text_stream *options) {
+void Indexes::add_indexing_notation_for_symbols(compiled_documentation *cd, text_stream *L, text_stream *style, text_stream *options) {
     IndexUtilities::add_span_notation(cd, L, NULL, style, INDEX_SYMBOLS_SPP);
     Indexes::add_category(cd, style, options, NULL);
 }
 
-void Indexes::add_indexing_notation_for_definitions(compiled_documentation *cd, text_stream *style, text_stream *options, text_stream *subdef) {
+void Indexes::add_indexing_notation_for_definitions(compiled_documentation *cd, text_stream *style, text_stream *options, text_stream *subdef) {
     TEMPORARY_TEXT(key)
     WRITE_TO(key, "!%S", subdef);
     if (Str::len(subdef) > 0) WRITE_TO(key, "-");
@@ -90,7 +90,7 @@ roughly to a CSS class used in the final output.
     DISCARD_TEXT(key)
 }
 
-void Indexes::add_indexing_notation_for_examples(compiled_documentation *cd, text_stream *style, text_stream *options) {
+void Indexes::add_indexing_notation_for_examples(compiled_documentation *cd, text_stream *style, text_stream *options) {
     Indexes::add_category(cd, style, options, I"!example");
 }
 
@@ -103,7 +103,7 @@ roughly to a CSS class used in the final output.
     struct linked_list *lemma_list;  of index_lemma
 } cd_indexing_data;
 
-cd_indexing_data Indexes::new_indexing_data(void) {
+cd_indexing_data Indexes::new_indexing_data(void) {
     cd_indexing_data id;
     id.present_with_index = FALSE;
     id.notations = NEW_LINKED_LIST(span_notation);
@@ -114,7 +114,7 @@ roughly to a CSS class used in the final output.
     return id;
 }
 
-int Indexes::indexing_occurred(compiled_documentation *cd) {
+int Indexes::indexing_occurred(compiled_documentation *cd) {
     return cd->id.present_with_index;
 }
 
@@ -233,7 +233,7 @@ category:

-void Indexes::scan(compiled_documentation *cd) {
+void Indexes::scan(compiled_documentation *cd) {
     markdown_item *latest = cd->markdown_content;
     int volume_number = -1;
     Indexes::scan_r(cd, cd->markdown_content, &latest, NULL, &volume_number);
diff --git a/inbuild/supervisor-module/Chapter 1/Supervisor Module.w b/inbuild/supervisor-module/Chapter 1/Supervisor Module.w
index f8483642c..894d98f90 100644
--- a/inbuild/supervisor-module/Chapter 1/Supervisor Module.w	
+++ b/inbuild/supervisor-module/Chapter 1/Supervisor Module.w	
@@ -15,6 +15,7 @@ which use this module:
 @e build_skill_CLASS
 @e build_step_CLASS
 @e build_vertex_CLASS
+@e cd_image_CLASS
 @e cd_layout_error_CLASS
 @e cd_pageset_CLASS
 @e cd_source_file_CLASS
@@ -56,6 +57,7 @@ DECLARE_CLASS(build_script)
 DECLARE_CLASS(build_skill)
 DECLARE_CLASS(build_step)
 DECLARE_CLASS(build_vertex)
+DECLARE_CLASS(cd_image)
 DECLARE_CLASS(cd_layout_error)
 DECLARE_CLASS(cd_pageset)
 DECLARE_CLASS(cd_source_file)
diff --git a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w
index fd03f8501..8a8e1cee2 100644
--- a/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w	
+++ b/inbuild/supervisor-module/Chapter 4/Extension Bundle Manager.w	
@@ -337,6 +337,8 @@ inbuild_copy *ExtensionBundleManager::claim_folder_as_copy(pathname *P, inbuild_
 				;
 			} else if (Str::eq(subdir, I"Tests")) {
 				;
+			} else if (Str::eq(subdir, I"Images")) {
+				;
 			} else {
 				TEMPORARY_TEXT(error_text)
 				WRITE_TO(error_text,
diff --git a/inbuild/supervisor-module/Chapter 7/Documentation Compiler.w b/inbuild/supervisor-module/Chapter 7/Documentation Compiler.w
index 510aa33ee..30968b6f5 100644
--- a/inbuild/supervisor-module/Chapter 7/Documentation Compiler.w	
+++ b/inbuild/supervisor-module/Chapter 7/Documentation Compiler.w	
@@ -66,6 +66,8 @@ typedef struct compiled_documentation {
 	struct pathname *domain; /* where the documentation source is */
 	struct linked_list *source_files; /* of |cd_source_file| */
 	struct linked_list *layout_errors; /* of |cd_layout_error| */
+	struct linked_list *images; /* of |cd_image| */
+	struct text_stream *images_URL;
 
 	struct linked_list *volumes; /* of |cd_volume| */
 	struct text_stream *contents_URL_pattern;
@@ -248,6 +250,56 @@ int DocumentationCompiler::scold(OUTPUT_STREAM, compiled_documentation *cd) {
 	return TRUE;
 }
 
+@ "Images" are image files, that is, pictures.
+
+=
+typedef struct cd_image {
+	struct filename *source;
+	struct text_stream *final_leafname;
+	struct text_stream *prefix;
+	struct text_stream *correct_URL;
+	int used;
+	CLASS_DEFINITION
+} cd_image;
+
+void DocumentationCompiler::add_images(compiled_documentation *cd, pathname *figures,
+	text_stream *prefix) {
+	linked_list *L = Directories::listing(figures);
+	text_stream *entry;
+	LOOP_OVER_LINKED_LIST(entry, text_stream, L) {
+		if (Platform::is_folder_separator(Str::get_last_char(entry)) == FALSE) {
+			cd_image *cdim = CREATE(cd_image);
+			cdim->source = Filenames::in(figures, entry);
+			LOOP_THROUGH_TEXT(pos, entry)
+				Str::put(pos, Characters::tolower(Str::get(pos)));
+			cdim->final_leafname = Str::duplicate(entry);
+			cdim->correct_URL = Str::new();
+			if (Str::len(prefix) > 0) {
+				WRITE_TO(cdim->correct_URL, "%S/%S", prefix, entry);
+			} else {
+				WRITE_TO(cdim->correct_URL, "%S", entry);
+			}
+			cdim->prefix = Str::duplicate(prefix);
+			cdim->used = FALSE;
+			ADD_TO_LINKED_LIST(cdim, cd_image, cd->images);
+			Markdown::create(cd->link_references, Str::duplicate(entry), cdim->correct_URL, NULL);
+		}
+	}
+}
+
+compiled_documentation *cd_being_watched_for_image_use = NULL;
+void DocumentationCompiler::watch_image_use(compiled_documentation *cd) {
+	cd_being_watched_for_image_use = cd;
+}
+void DocumentationCompiler::notify_image_use(text_stream *URL) {
+	if (cd_being_watched_for_image_use) {
+		cd_image *cdim;
+		LOOP_OVER_LINKED_LIST(cdim, cd_image, cd_being_watched_for_image_use->images)
+			if (Str::eq(URL, cdim->correct_URL))
+				cdim->used = TRUE;
+	}
+}
+
 @ And we can now create a new cd object.
 
 =
@@ -259,6 +311,7 @@ compiled_documentation *DocumentationCompiler::new_cd(pathname *P,
 		cd_source_file *Documentation_md_cdsf = NULL;
 		@;
 		@;
+		DocumentationCompiler::add_images(cd, Pathnames::down(P, I"Images"), cd->images_URL);
 	}
 	Indexes::add_indexing_notation(cd, NULL, NULL, I"standard", NULL);
 	Indexes::add_indexing_notation(cd, I"@", NULL, I"name", I"(invert)");
@@ -316,7 +369,9 @@ compiled_documentation *DocumentationCompiler::new_cd(pathname *P,
 	cd->duplex_contents_page = FALSE;
 	cd->source_files = NEW_LINKED_LIST(cd_source_file);
 	cd->domain = P;
-
+	cd->images = NEW_LINKED_LIST(cd_image);
+	cd->images_URL = I"images";
+	
 @ =
 	linked_list *L = Directories::listing(P);
 	text_stream *entry;
@@ -460,6 +515,8 @@ void DocumentationCompiler::read_sitemap_helper(text_stream *cl, text_file_posit
 
 	if (Regexp::match(&mr, cl, U" *cross-references: to \"(%c*)\"")) {
 		cd->xrefs_file_pattern = Str::duplicate(mr.exp[0]);
+	} else if (Regexp::match(&mr, cl, U" *images: to \"(%c*)\"")) {
+		cd->images_URL = Str::duplicate(mr.exp[0]);
 	} else if (Regexp::match(&mr, cl, U" *contents: *(%c+?) to \"(%c*)\"")) {
 		if (Str::eq(mr.exp[0], I"standard")) cd->duplex_contents_page = FALSE;
 		else if (Str::eq(mr.exp[0], I"duplex")) cd->duplex_contents_page = TRUE;
@@ -900,6 +957,7 @@ perform Phase II on everything.
 
 =
 void DocumentationCompiler::compile_inner(compiled_documentation *cd) {
+	DocumentationCompiler::watch_image_use(cd);
 	/* Phase I parsing */
 	DocumentationCompiler::Phase_I_on_volumes(cd);
 	DocumentationCompiler::detect_satellites(cd);
@@ -932,6 +990,7 @@ void DocumentationCompiler::compile_inner(compiled_documentation *cd) {
 	if (Indexes::indexing_occurred(cd)) cd->include_index[GENERAL_INDEX] = TRUE;
 	if (LinkedLists::len(cd->examples) >= 10) cd->include_index[NUMERICAL_EG_INDEX] = TRUE;
 	if (LinkedLists::len(cd->examples) >= 20) cd->include_index[ALPHABETICAL_EG_INDEX] = TRUE;
+	DocumentationCompiler::watch_image_use(NULL);
 }
 
 @ In addition to regular Phase I parsing of the content in the volumes, we
diff --git a/inbuild/supervisor-module/Chapter 7/Documentation Renderer.w b/inbuild/supervisor-module/Chapter 7/Documentation Renderer.w
index c41d2410b..10d13962b 100644
--- a/inbuild/supervisor-module/Chapter 7/Documentation Renderer.w	
+++ b/inbuild/supervisor-module/Chapter 7/Documentation Renderer.w	
@@ -40,6 +40,7 @@ void DocumentationRenderer::as_HTML(pathname *P, compiled_documentation *cd, tex
 		Languages::set_default_directory(LP);
 	}
 	if (cd) {
+		DocumentationCompiler::watch_image_use(cd);
 		text_stream *OUT = DocumentationRenderer::open_subpage(P, cd->contents_URL_pattern);
 		if (OUT) {
 			markdown_item *md = NULL;
@@ -155,6 +156,15 @@ void DocumentationRenderer::as_HTML(pathname *P, compiled_documentation *cd, tex
 				STREAM_CLOSE(XR);
 			}
 		}
+		cd_image *cdim;
+		LOOP_OVER_LINKED_LIST(cdim, cd_image, cd->images)
+			if (cdim->used) {
+				pathname *Q = P;
+				if (Str::len(cdim->prefix) > 0) Q = Pathnames::down(Q, cdim->prefix);
+				Pathnames::create_in_file_system(Q);
+				filename *T = Filenames::in(Q, cdim->final_leafname);
+				BinaryFiles::copy(cdim->source, T, TRUE);
+			}
 	}
 }
 
diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt
index e04ebd635..24413a87e 100644
--- a/inform7/Figures/memory-diagnostics.txt
+++ b/inform7/Figures/memory-diagnostics.txt
@@ -1,44 +1,44 @@
-Total memory consumption was 141958K = 139 MB
+Total memory consumption was 139600K = 136 MB
 
- ---- was used for 2127253 objects, in 374807 frames in 0 x 800K = 0K = 0 MB:
+ ---- was used for 2127256 objects, in 374810 frames in 0 x 800K = 0K = 0 MB:
 
-    29.7%  inter_tree_node_array                    60 x 8192 = 491520 objects, 43255680 bytes
-    19.0%  text_stream_array                        4926 x 100 = 492600 objects, 27743232 bytes
-    17.4%  linked_list                              45406 objects, 25427360 bytes
-     9.8%  inter_symbol_array                       134 x 1024 = 137216 objects, 14274752 bytes
-     9.5%  inter_error_stash_array                  106 x 1024 = 108544 objects, 13897024 bytes
-     7.3%  parse_node                               133803 objects, 10704240 bytes
-     5.2%  verb_conjugation                         164 objects, 7610912 bytes
-     3.9%  parse_node_annotation_array              357 x 500 = 178500 objects, 5723424 bytes
-     3.0%  scan_directory                           1091 objects, 4503648 bytes
+    30.2%  inter_tree_node_array                    60 x 8192 = 491520 objects, 43255680 bytes
+    19.4%  text_stream_array                        4926 x 100 = 492600 objects, 27743232 bytes
+    17.7%  linked_list                              45409 objects, 25429040 bytes
+     9.9%  inter_symbol_array                       134 x 1024 = 137216 objects, 14274752 bytes
+     9.7%  inter_error_stash_array                  106 x 1024 = 108544 objects, 13897024 bytes
+     7.4%  parse_node                               133803 objects, 10704240 bytes
+     5.3%  verb_conjugation                         164 objects, 7610912 bytes
+     4.0%  parse_node_annotation_array              357 x 500 = 178500 objects, 5723424 bytes
+     3.1%  scan_directory                           1091 objects, 4503648 bytes
      2.4%  pcalc_prop_array                         26 x 1000 = 26000 objects, 3536832 bytes
      2.2%  inter_name_array                         67 x 1000 = 67000 objects, 3218144 bytes
-     1.8%  kind_array                               68 x 1000 = 68000 objects, 2722176 bytes
-     1.4%  inter_schema_token                       14164 objects, 2152928 bytes
+     1.9%  kind_array                               68 x 1000 = 68000 objects, 2722176 bytes
+     1.5%  inter_schema_token                       14164 objects, 2152928 bytes
      1.4%  inter_name_generator_array               51 x 1000 = 51000 objects, 2041632 bytes
-     1.2%  package_request                          21353 objects, 1879064 bytes
+     1.3%  package_request                          21353 objects, 1879064 bytes
      1.2%  vocabulary_entry_array                   163 x 100 = 16300 objects, 1830816 bytes
      1.1%  dict_entry_array                         501 x 100 = 50100 objects, 1619232 bytes
      1.0%  inter_symbols_table                      26887 objects, 1505672 bytes
      1.0%  match_trie_array                         11 x 1000 = 11000 objects, 1496352 bytes
-     0.9%  i6_schema_array                          24 x 100 = 2400 objects, 1440768 bytes
-     0.8%  inter_package                            26887 objects, 1290576 bytes
+     1.0%  i6_schema_array                          24 x 100 = 2400 objects, 1440768 bytes
+     0.9%  inter_package                            26887 objects, 1290576 bytes
      0.7%  map_data                                 677 objects, 1137360 bytes
      0.7%  id_body                                  980 objects, 1121120 bytes
      0.7%  adjective_meaning                        208 objects, 1030016 bytes
-     0.6%  inter_schema_node                        9062 objects, 1014944 bytes
+     0.7%  inter_schema_node                        9062 objects, 1014944 bytes
      0.6%  excerpt_meaning                          3165 objects, 987480 bytes
      0.6%  production                               3985 objects, 924520 bytes
      0.6%  ptoken                                   8652 objects, 899808 bytes
      0.6%  grammatical_usage                        3648 objects, 875520 bytes
-     0.5%  individual_form                          2581 objects, 867216 bytes
+     0.6%  individual_form                          2581 objects, 867216 bytes
      0.4%  unary_predicate_array                    17 x 1000 = 17000 objects, 680544 bytes
      0.3%  local_variable_array                     49 x 100 = 4900 objects, 471968 bytes
      0.2%  verb_usage                               1148 objects, 394912 bytes
      0.2%  rule                                     478 objects, 374752 bytes
      0.2%  dictionary                               7767 objects, 372816 bytes
      0.2%  verb_form                                388 objects, 350752 bytes
-     0.1%  noun                                     2395 objects, 287400 bytes
+     0.2%  noun                                     2395 objects, 287400 bytes
      0.1%  compilation_subtask                      3388 objects, 271040 bytes
      0.1%  inference_subject                        672 objects, 263424 bytes
      0.1%  inter_annotation_array                   2 x 8192 = 16384 objects, 262208 bytes
@@ -66,8 +66,8 @@ Total memory consumption was 141958K = 139 MB
      ----  spatial_data                             677 objects, 64992 bytes
      ----  kind_constructor                         79 objects, 64464 bytes
      ----  linked_list_item_array                   4 x 1000 = 4000 objects, 64128 bytes
-     ----  actions_rcd_data                         1960 objects, 62720 bytes
      ----  scenes_rcd_data                          1960 objects, 62720 bytes
+     ----  actions_rcd_data                         1960 objects, 62720 bytes
      ----  booking                                  868 objects, 62496 bytes
      ----  kind_macro_definition                    9 objects, 62280 bytes
      ----  command_grammar                          130 objects, 58240 bytes
@@ -83,8 +83,8 @@ Total memory consumption was 141958K = 139 MB
      ----  heading                                  213 objects, 47712 bytes
      ----  to_family_data                           528 objects, 42240 bytes
      ----  text_substitution                        438 objects, 42048 bytes
-     ----  anl_clause_array                         1 x 1000 objects, 40032 bytes
      ----  activity_list_array                      1 x 1000 objects, 40032 bytes
+     ----  anl_clause_array                         1 x 1000 objects, 40032 bytes
      ----  shared_variable_access_list_array        12 x 100 = 1200 objects, 38784 bytes
      ----  parsing_data                             677 objects, 37912 bytes
      ----  production_list                          627 objects, 35112 bytes
@@ -111,8 +111,8 @@ Total memory consumption was 141958K = 139 MB
      ----  pcalc_prop_deferral                      86 objects, 17888 bytes
      ----  to_phrase_request                        63 objects, 17136 bytes
      ----  understanding_reference_array            2 x 100 = 200 objects, 16064 bytes
-     ----  match_avinue_array                       1 x 1000 objects, 16032 bytes
      ----  action_name_list_array                   1 x 1000 objects, 16032 bytes
+     ----  match_avinue_array                       1 x 1000 objects, 16032 bytes
      ----  md_doc_state                             3 objects, 15720 bytes
      ----  adjective                                140 objects, 15680 bytes
      ----  JSON_value                               174 objects, 15312 bytes
@@ -141,13 +141,13 @@ Total memory consumption was 141958K = 139 MB
      ----  explicit_action_array                    1 x 100 objects, 4832 bytes
      ----  value_property_data                      86 objects, 4816 bytes
      ----  compatibility_specification              100 objects, 4800 bytes
-     ----  method_set                               144 objects, 4608 bytes
      ----  parsing_pp_data                          96 objects, 4608 bytes
+     ----  method_set                               144 objects, 4608 bytes
      ----  command_line_switch                      56 objects, 4480 bytes
      ----  semver_range                             42 objects, 4368 bytes
      ----  use_option                               31 objects, 4216 bytes
-     ----  parse_node_annotation_type               124 objects, 3968 bytes
      ----  either_or_property_data                  62 objects, 3968 bytes
+     ----  parse_node_annotation_type               124 objects, 3968 bytes
      ----  definition                               48 objects, 3456 bytes
      ----  property_setting_bp_data                 86 objects, 3440 bytes
      ----  submodule_request                        86 objects, 3440 bytes
@@ -157,23 +157,23 @@ Total memory consumption was 141958K = 139 MB
      ----  JSON_single_requirement                  55 objects, 2640 bytes
      ----  parentage_inference_data                 79 objects, 2528 bytes
      ----  part_of_inference_data                   79 objects, 2528 bytes
-     ----  kind_constructor_instance_rule_array     1 x 100 objects, 2432 bytes
      ----  kind_constructor_casting_rule_array      1 x 100 objects, 2432 bytes
+     ----  kind_constructor_instance_rule_array     1 x 100 objects, 2432 bytes
      ----  equation_symbol                          30 objects, 2400 bytes
      ----  scene                                    1 object, 2352 bytes
      ----  build_step                               28 objects, 2016 bytes
-     ----  compiler_feature                         30 objects, 1680 bytes
      ----  pronoun_usage                            42 objects, 1680 bytes
+     ----  compiler_feature                         30 objects, 1680 bytes
      ----  table_contribution_array                 1 x 100 objects, 1632 bytes
      ----  markdown_feature                         33 objects, 1584 bytes
      ----  inform_pipeline                          24 objects, 1536 bytes
      ----  inbuild_requirement                      37 objects, 1480 bytes
      ----  noun_filter_token                        22 objects, 1408 bytes
-     ----  special_meaning_holder                   35 objects, 1400 bytes
      ----  inter_node_array                         35 objects, 1400 bytes
+     ----  special_meaning_holder                   35 objects, 1400 bytes
      ----  JSON_requirement                         42 objects, 1344 bytes
-     ----  constant_phrase                          20 objects, 1280 bytes
      ----  table_column                             16 objects, 1280 bytes
+     ----  constant_phrase                          20 objects, 1280 bytes
      ----  invocation_options_array                 1 x 100 objects, 1224 bytes
      ----  direction_inference_data                 30 objects, 1200 bytes
      ----  inbuild_search_result                    29 objects, 1160 bytes
@@ -185,7 +185,7 @@ Total memory consumption was 141958K = 139 MB
      ----  pipeline_stage                           20 objects, 960 bytes
      ----  named_rulebook_outcome                   15 objects, 960 bytes
      ----  JSON_pair_requirement                    29 objects, 928 bytes
-     ----  compiled_documentation                   3 objects, 864 bytes
+     ----  compiled_documentation                   3 objects, 912 bytes
      ----  control_structure_phrase                 12 objects, 864 bytes
      ----  kit_configuration                        21 objects, 840 bytes
      ----  cached_understanding                     21 objects, 840 bytes
@@ -198,46 +198,46 @@ Total memory consumption was 141958K = 139 MB
      ----  chapter_md                               7 objects, 616 bytes
      ----  code_generation                          1 object, 576 bytes
      ----  module                                   7 objects, 560 bytes
-     ----  inter_annotation_form                    14 objects, 560 bytes
      ----  generated_segment                        14 objects, 560 bytes
      ----  inter_warehouse_room                     10 objects, 560 bytes
+     ----  inter_annotation_form                    14 objects, 560 bytes
      ----  rulebook_outcome                         17 objects, 544 bytes
-     ----  markdown_variation                       3 objects, 528 bytes
-     ----  small_word_set                           11 objects, 528 bytes
      ----  indexing_category                        6 objects, 528 bytes
+     ----  small_word_set                           11 objects, 528 bytes
+     ----  markdown_variation                       3 objects, 528 bytes
      ----  IFM_example                              4 objects, 512 bytes
      ----  equation                                 4 objects, 480 bytes
      ----  i6_memory_setting                        15 objects, 480 bytes
      ----  bp_family                                14 objects, 448 bytes
      ----  inbuild_genre                            8 objects, 448 bytes
-     ----  source_file                              5 objects, 440 bytes
      ----  inference_family                         11 objects, 440 bytes
+     ----  source_file                              5 objects, 440 bytes
      ----  article_usage                            8 objects, 384 bytes
+     ----  cached_kind_declaration                  8 objects, 320 bytes
+     ----  tree_inventory                           1 object, 320 bytes
      ----  module_request                           8 objects, 320 bytes
      ----  door_dir_notice                          5 objects, 320 bytes
-     ----  tree_inventory                           1 object, 320 bytes
      ----  pronoun                                  8 objects, 320 bytes
-     ----  cached_kind_declaration                  8 objects, 320 bytes
      ----  grammatical_category                     8 objects, 320 bytes
      ----  inter_pipeline                           1 object, 312 bytes
      ----  up_family                                9 objects, 288 bytes
      ----  compilation_unit                         5 objects, 280 bytes
+     ----  door_to_notice                           5 objects, 280 bytes
      ----  contents_entry                           7 objects, 280 bytes
      ----  explicit_bp_data                         5 objects, 280 bytes
-     ----  door_to_notice                           5 objects, 280 bytes
      ----  verb_usage_tier                          5 objects, 240 bytes
      ----  kit_dependency                           5 objects, 240 bytes
      ----  inform_project                           1 object, 232 bytes
      ----  adjective_meaning_family                 7 objects, 224 bytes
-     ----  index_reference                          4 objects, 224 bytes
-     ----  index_lemma                              4 objects, 224 bytes
      ----  local_block_value                        4 objects, 224 bytes
+     ----  index_lemma                              4 objects, 224 bytes
+     ----  index_reference                          4 objects, 224 bytes
      ----  cd_volume                                3 objects, 216 bytes
      ----  test_scenario                            1 object, 216 bytes
      ----  release_instructions                     1 object, 208 bytes
      ----  attachment_instruction                   5 objects, 200 bytes
-     ----  build_skill                              5 objects, 200 bytes
      ----  code_generator                           5 objects, 200 bytes
+     ----  build_skill                              5 objects, 200 bytes
      ----  element_activation                       6 objects, 192 bytes
      ----  plural_dictionary_entry                  4 objects, 192 bytes
      ----  imperative_defn_family                   4 objects, 160 bytes
@@ -247,40 +247,40 @@ Total memory consumption was 141958K = 139 MB
      ----  inbuild_nest                             3 objects, 120 bytes
      ----  inform_language                          1 object, 104 bytes
      ----  md_links_dictionary                      3 objects, 96 bytes
-     ----  group_together_function                  2 objects, 80 bytes
-     ----  compile_task_data                        1 object, 80 bytes
      ----  article                                  2 objects, 80 bytes
+     ----  compile_task_data                        1 object, 80 bytes
+     ----  group_together_function                  2 objects, 80 bytes
      ----  figures_data                             1 object, 56 bytes
      ----  inter_warehouse                          1 object, 56 bytes
      ----  build_methodology                        1 object, 56 bytes
-     ----  HTML_file_state                          1 object, 48 bytes
-     ----  star_invention                           1 object, 48 bytes
-     ----  inform_kit_ittt                          1 object, 48 bytes
      ----  I6_generation_data                       1 object, 48 bytes
-     ----  loop_over_scope                          1 object, 40 bytes
-     ----  target_pragma_setting                    1 object, 40 bytes
+     ----  star_invention                           1 object, 48 bytes
+     ----  HTML_file_state                          1 object, 48 bytes
+     ----  inform_kit_ittt                          1 object, 48 bytes
      ----  by_function_bp_data                      1 object, 40 bytes
+     ----  target_pragma_setting                    1 object, 40 bytes
+     ----  loop_over_scope                          1 object, 40 bytes
      ----  kind_template_definition                 1 object, 40 bytes
 
 100.0% was used for memory not allocated for objects:
 
-    63.0%  text stream storage                      91616632 bytes in 513052 claims
-     3.7%  dictionary storage                       5497920 bytes in 7767 claims
+    62.4%  text stream storage                      89201916 bytes in 512710 claims
+     3.8%  dictionary storage                       5497920 bytes in 7767 claims
      ----  sorting                                  2624 bytes in 531 claims
-     4.9%  source text                              7200000 bytes in 3 claims
-     7.4%  source text details                      10800000 bytes in 2 claims
+     5.0%  source text                              7200000 bytes in 3 claims
+     7.5%  source text details                      10800000 bytes in 2 claims
      0.1%  documentation fragments                  262144 bytes in 1 claim
      ----  linguistic stock array                   81920 bytes in 2 claims
      ----  small word set array                     105600 bytes in 22 claims
      3.1%  inter symbols storage                    4572416 bytes in 28261 claims
-    11.5%  inter bytecode storage                   16757556 bytes in 15 claims
-     4.2%  inter links storage                      6222976 bytes in 11 claims
+    11.7%  inter bytecode storage                   16757556 bytes in 15 claims
+     4.3%  inter links storage                      6222976 bytes in 11 claims
      0.1%  inter tree location list storage         191232 bytes in 32 claims
-     1.1%  instance-of-kind counting                1731856 bytes in 1 claim
+     1.2%  instance-of-kind counting                1731856 bytes in 1 claim
      ----  compilation workspace for objects        21936 bytes in 25 claims
      ----  lists for type-checking invocations      16000 bytes in 1 claim
      ----  code generation workspace for objects    3528 bytes in 19 claims
      0.1%  emitter array storage                    281184 bytes in 2006 claims
 
--134.-3% was overhead - -195255408 bytes = -190679K = -186 MB
+-136.-5% was overhead - -195257136 bytes = -190680K = -186 MB
 
diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt
index 80d95aad0..5d07a8efd 100644
--- a/inform7/Figures/timings-diagnostics.txt
+++ b/inform7/Figures/timings-diagnostics.txt
@@ -1,29 +1,28 @@
 100.0% in inform7 run
-     67.6% in compilation to Inter
-         45.6% in //Sequence::undertake_queued_tasks//
+     68.1% in compilation to Inter
+         46.1% in //Sequence::undertake_queued_tasks//
           4.3% in //MajorNodes::pre_pass//
-          3.5% in //MajorNodes::pass_1//
-          2.1% in //RTPhrasebook::compile_entries//
-          1.7% in //ImperativeDefinitions::assess_all//
+          3.6% in //MajorNodes::pass_1//
+          1.8% in //ImperativeDefinitions::assess_all//
           1.4% in //RTKindConstructors::compile//
+          1.4% in //RTPhrasebook::compile_entries//
           1.0% in //Sequence::lint_inter//
+          0.7% in //CompletionModule::compile//
           0.7% in //ImperativeDefinitions::compile_first_block//
           0.7% in //Sequence::undertake_queued_tasks//
-          0.3% in //CompletionModule::compile//
           0.3% in //MajorNodes::pass_2//
           0.3% in //Sequence::undertake_queued_tasks//
           0.3% in //World::stage_V//
-          4.7% not specifically accounted for
-     27.3% in running Inter pipeline
-          8.9% in step 14/15: generate inform6 -> auto.inf
-          6.8% in step 5/15: load-binary-kits
-          5.7% in step 6/15: make-synoptic-module
-          1.7% in step 9/15: make-identifiers-unique
-          0.3% in step 11/15: eliminate-redundant-labels
+          4.8% not specifically accounted for
+     27.4% in running Inter pipeline
+          9.1% in step 14/15: generate inform6 -> auto.inf
+          6.9% in step 5/15: load-binary-kits
+          5.8% in step 6/15: make-synoptic-module
+          1.8% in step 9/15: make-identifiers-unique
           0.3% in step 12/15: eliminate-redundant-operations
           0.3% in step 4/15: compile-splats
           0.3% in step 7/15: shorten-wiring
           0.3% in step 8/15: detect-indirect-calls
           2.2% not specifically accounted for
-      4.3% in supervisor
-      0.8% not specifically accounted for
+      4.0% in supervisor
+      0.4% not specifically accounted for
diff --git a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
index 3d4eba391..3687c36f8 100644
--- a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
+++ b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "Architecture16Kit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "compatibility": "16-bit",
     "kit-details": {
diff --git a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
index b5e525d77..fb9027b23 100644
--- a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
+++ b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "Architecture32Kit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "compatibility": "32-bit",
     "kit-details": {
diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
index 9dc6ebed6..dad15a9a8 100644
--- a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
+++ b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "BasicInformKit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "needs": [ {
         "need": {
diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
index f05f3a38c..5059776fd 100644
--- a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
+++ b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "CommandParserKit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "needs": [ {
         "need": {
diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
index b846a8efd..1b3cdf83e 100644
--- a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
+++ b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "EnglishLanguageKit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "needs": [ {
         "need": {
diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
index d94ae5276..ac58269b8 100644
--- a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
+++ b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
@@ -2,7 +2,7 @@
     "is": {
         "type": "kit",
         "title": "WorldModelKit",
-        "version": "10.2.0-beta+6X19"
+        "version": "10.2.0-beta+6X20"
     },
     "needs": [ {
         "need": {