An index is generated by interpreting an index structure file.


§1. The index is generated by a bare-bones structure file. These are identified by name: in particular Basic is the barer version, for Basic Inform projects, while Standard is the index as generated to suit typical IF uses of the Standard Rules — so, in other words, that's the index which most Inform users know. These names have to correspond to .indext structure files stored in the Inform distribution.

The structure file really just shows which index elements to include on which pages. For example:

page Kinds e9cf08
element Ch
element Ar
element Vl

declares a new index page with leafname Kinds.html, and with the highlight colour (in CSS hexadecimal style) e9cf08; and then declares that it contains the three elements Ch, Ar and Vl, in that order. Note that this file does not contain any user-facing text: all of that comes from Localisation (in html).

The file also doesn't contain instructions for what goes into those elements. All of that is hardwired into this module's code: for example, Ar is generated by the function ArithmeticElement::render in Arithmetic Element.

The method here is basically to read all the declarations, generating a set of index_page and index_element objects to represent them, and only to begin writing some HTML when this scanning is done, i.e., when the whole structure file has been read.

void InterpretIndex::generate(text_stream *structure, index_session *session) {
    localisation_dictionary *D = Indexing::get_localisation(session);
    Indexing::empty_list_of_pages(session);
    filename *index_structure = InstalledFiles::index_structure_file(structure);
    Read the structure file line by line1.1;
    Actually generate the index files1.2;
}

§1.1. Read the structure file line by line1.1 =

    TextFiles::read(index_structure, FALSE, "unable to read index structure file", TRUE,
        &InterpretIndex::read_structure, NULL, (void *) session);

§2. contents is currently implemented identically to pages, but it is supposed to represent the home page of the index. The declaration for it should come last in the structure file, like so:

contents Welcome 111111

It has no elements, because its only content will be a menu of links to the other pages.

void InterpretIndex::read_structure(text_stream *text, text_file_position *tfp, void *state) {
    index_session *session = (index_session *) state;
    localisation_dictionary *D = Indexing::get_localisation(session);
    match_results mr = Regexp::create_mr();
    if (Regexp::match(&mr, text, U"page (%C+) (%C+)")) {
        Register a new index page2.1;
    } else if (Regexp::match(&mr, text, U"contents (%C+) (%C+)")) {
        Register a new index page2.1;
    } else if (Regexp::match(&mr, text, U"element (%C+)")) {
        Register a new index element2.2;
    }
    Regexp::dispose_of(&mr);
}

§2.1. Note that the heading written on Kinds.html, say, is drawn from the localisation file under the key %Title in the context %%Kinds. In English, that will be "Kinds", but if the index is being generated with a Dutch localisation dictionary then perhaps it would be "Soorten". Under the hood, though, the filename will be Kinds.html either way.

Register a new index page2.1 =

    text_stream *col = mr.exp[1];
    text_stream *key = mr.exp[0];
    TEMPORARY_TEXT(path)
    WRITE_TO(path, "Index.Pages.%S.Title", key);
    text_stream *heading = Localisation::read(D, path);
    Str::clear(path);
    WRITE_TO(path, "Index.Pages.%S.Caption", key);
    text_stream *explanation = Localisation::read(D, path);
    DISCARD_TEXT(path)
    InterpretIndex::register_page(col, heading, explanation, key, session);

§2.2. Register a new index element2.2 =

    index_page *latest = Indexing::latest_page(session);
    if (latest == NULL) internal_error("element without page");
    text_stream *elt = mr.exp[0];
    TEMPORARY_TEXT(tkey)
    TEMPORARY_TEXT(hkey)
    WRITE_TO(tkey, "Index.Elements.%S.Title", elt);
    WRITE_TO(hkey, "Index.Elements.%S.Heading", elt);
    InterpretIndex::register_element(elt, latest, Localisation::read(D, tkey), hkey);
    DISCARD_TEXT(tkey)
    DISCARD_TEXT(hkey)

§1.2. Actually generate the index files1.2 =

    index_page *page;
    linked_list *L = Indexing::get_list_of_pages(session);
    LOOP_OVER_LINKED_LIST(page, index_page, L) {
        TEMPORARY_TEXT(leafname)
        WRITE_TO(leafname, "%S.html", page->page_leafname);
        TEMPORARY_TEXT(key)
        WRITE_TO(key, "Index.Pages.%S.Title", page->page_leafname);
        text_stream index_file_struct; text_stream *OUT = &index_file_struct;
        InterpretIndex::open_file(OUT, page, leafname, Localisation::read(D, key),
            -1, session);
        Elements::periodic_table(OUT, page, leafname, session);
        InterpretIndex::close_index_file(OUT);
        DISCARD_TEXT(key)
        DISCARD_TEXT(leafname)
    }
    GroupedElement::detail_pages(session);

§3. Registering. So, then, here are the objects representing the index pages and their elements of content:

typedef struct index_page {
    int no_elements;
    struct text_stream *key_colour;
    struct text_stream *page_title;
    struct text_stream *page_explanation;
    struct text_stream *page_leafname;
    struct linked_list *elements;  of index_element
    struct index_session *for_session;
    CLASS_DEFINITION
} index_page;

index_page *InterpretIndex::register_page(text_stream *col, text_stream *title,
    text_stream *exp, text_stream *leaf, index_session *session) {
    index_page *new_page = CREATE(index_page);
    new_page->no_elements = 0;
    new_page->key_colour = Str::duplicate(col);
    new_page->page_title = Str::duplicate(title);
    new_page->page_explanation = Str::duplicate(exp);
    new_page->page_leafname = Str::duplicate(leaf);
    new_page->elements = NEW_LINKED_LIST(index_element);
    new_page->for_session = session;
    Indexing::add_page(session, new_page);
    return new_page;
}

§4.

typedef struct index_element {
    int atomic_number;  1, 2, 3, ..., within its page
    struct text_stream *chemical_symbol;
    struct text_stream *element_name;
    struct text_stream *explanation_key;
    struct index_page *owning_page;
    CLASS_DEFINITION
} index_element;

index_element *InterpretIndex::register_element(text_stream *abb, index_page *owner,
    text_stream *title, text_stream *explanation) {
    if (owner == NULL)
        internal_error("template creates index elements improperly");
    if (Str::len(abb) > 2)
        internal_error("abbreviation for index element too long");
    index_element *ie = CREATE(index_element);
    ie->owning_page = owner;
    ie->atomic_number = ++(owner->no_elements);
    ie->chemical_symbol = Str::duplicate(abb);
    ie->element_name = Str::duplicate(title);
    ie->explanation_key = Str::duplicate(explanation);
    ADD_TO_LINKED_LIST(ie, index_element, owner->elements);
    return ie;
}

§5. Opening and closing HTML files.

text_stream *InterpretIndex::open_file(text_stream *IF,
    index_page *page, text_stream *index_leaf,
    text_stream *title, int sub, index_session *session) {
    filename *F = IndexLocations::filename(index_leaf, sub);
    if (STREAM_OPEN_TO_FILE(IF, F, UTF8_ENC) == FALSE) {
        #ifdef CORE_MODULE
        Problems::fatal_on_file("Can't open index file", F);
        #endif
        #ifndef CORE_MODULE
        Errors::fatal_with_file("can't open index file", F);
        #endif
    }
    InformPages::header(IF, title, JAVASCRIPT_FOR_STANDARD_PAGES_IRES, (void *) page);
    IndexUtilities::clear_page_data(session);
    return IF;
}

§6.

void InterpretIndex::close_index_file(text_stream *ifl) {
    InformPages::footer(ifl);
    STREAM_CLOSE(ifl);
}