1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-30 22:14:58 +03:00

Written about half of an indoc manual

This commit is contained in:
Graham Nelson 2019-02-17 14:08:19 +00:00
parent 0e06463b2f
commit 9dbeba91b7
18 changed files with 503 additions and 314 deletions

View file

@ -30,7 +30,7 @@ void Basics::end(void) {
@h Setting up the memory manager.
We need to itemise the structures we'll want to allocate:
@e indoc_instructions_MT
@e settings_block_MT
@e volume_MT
@e chapter_MT
@e section_MT
@ -49,7 +49,7 @@ We need to itemise the structures we'll want to allocate:
@ And then expand:
=
ALLOCATE_INDIVIDUALLY(indoc_instructions)
ALLOCATE_INDIVIDUALLY(settings_block)
ALLOCATE_INDIVIDUALLY(volume)
ALLOCATE_INDIVIDUALLY(chapter)
ALLOCATE_INDIVIDUALLY(section)

View file

@ -14,7 +14,7 @@ void Configuration::add_instructions_file(filename *F) {
ADD_TO_LINKED_LIST(F, filename, instructions_files);
}
void Configuration::read_instructions(text_stream *target, indoc_instructions *settings) {
void Configuration::read_instructions(text_stream *target, settings_block *settings) {
Instructions::read_instructions(target, instructions_files, settings);
}
@ -23,18 +23,16 @@ void Configuration::read_instructions(text_stream *target, indoc_instructions *s
=
typedef struct cl_state {
struct text_stream *target_chosen;
struct indoc_instructions *settings;
struct settings_block *settings;
} cl_state;
void Configuration::read_command_line(int argc, char **argv, indoc_instructions *settings) {
pathname *IM = Pathnames::from_text(I"indoc");
IM = Pathnames::subfolder(IM, I"Materials");
filename *basics = Filenames::in_folder(IM, I"basic-instructions.txt");
Configuration::add_instructions_file(basics);
void Configuration::read_command_line(int argc, char **argv, settings_block *settings) {
cl_state state;
state.target_chosen = Str::new();
state.settings = settings;
@<Read the command line@>;
Configuration::add_instructions_file(
Filenames::in_folder(path_to_indoc_materials, I"basic-instructions.txt"));
Configuration::add_instructions_file(
Filenames::in_folder(settings->book_folder, I"indoc-instructions.txt"));
Configuration::read_instructions(state.target_chosen, settings);
@ -73,7 +71,7 @@ void Configuration::read_command_line(int argc, char **argv, indoc_instructions
@ =
void Configuration::switch(int id, int val, text_stream *arg, void *v_cl_state) {
indoc_instructions *settings = ((cl_state *) v_cl_state)->settings;
settings_block *settings = ((cl_state *) v_cl_state)->settings;
switch (id) {
case VERBOSE_CLSW: settings->verbose_mode = val; break;
case TEST_INDEX_CLSW: settings->test_index_mode = val; break;

View file

@ -3,8 +3,10 @@
Instructions of indoc to different output types.
@h Definitions.
@ A few fundamental options set at the command line.
The command-line and Instructions-file-set values provide a large slate of
what used to be global variables in the Perl version of Indoc. Today they
are herded together into an instance of the |settings_block| structure,
and in particular into a global instance of this called |indoc_settings|.
@d LETTER_ALPHABETIZATION 1
@d WORD_ALPHABETIZATION 2
@ -29,7 +31,7 @@ Instructions of indoc to different output types.
@d WRAPPER_zip 3
=
typedef struct indoc_instructions {
typedef struct settings_block {
int verbose_mode;
int test_index_mode;
@ -85,13 +87,13 @@ typedef struct indoc_instructions {
struct navigation_design *navigation;
MEMORY_MANAGEMENT
} indoc_instructions;
} settings_block;
@
=
indoc_instructions *Instructions::clean_slate(void) {
indoc_instructions *settings = CREATE(indoc_instructions);
settings_block *Instructions::clean_slate(void) {
settings_block *settings = CREATE(settings_block);
settings->verbose_mode = FALSE;
settings->test_index_mode = FALSE;
@ -145,18 +147,11 @@ indoc_instructions *Instructions::clean_slate(void) {
settings->wrapper = WRAPPER_none;
settings->ebook = NULL;
settings->navigation = Gadgets::default();
settings->navigation = Nav::default();
return settings;
}
@ =
typedef struct dc_metadatum {
struct text_stream *dc_key;
struct text_stream *dc_val;
MEMORY_MANAGEMENT
} dc_metadatum;
@h Instructions file.
Note that |indoc| reports errors in the instructions file, but doesn't halt on
them until all have been found. (The user may as well get all of the bad news,
@ -164,13 +159,12 @@ not just the beginning of it.)
=
void Instructions::read_instructions(text_stream *target_sought, linked_list *L,
indoc_instructions *settings) {
settings_block *settings) {
int found_flag = FALSE; /* was a target of this name actually found? */
settings->change_logs_folder = Pathnames::subfolder(settings->book_folder, I"Change Logs");
settings->examples_directory = Pathnames::subfolder(settings->book_folder, I"Examples");
pathname *Materials = Pathnames::subfolder(Pathnames::from_text(I"indoc"), I"Materials");
settings->css_source_file = Filenames::in_folder(Materials, I"base.css");
settings->css_source_file = Filenames::in_folder(path_to_indoc_materials, I"base.css");
settings->definitions_index_leafname = Str::duplicate(I"general_index.html");
filename *F;
@ -181,7 +175,7 @@ void Instructions::read_instructions(text_stream *target_sought, linked_list *L,
@<Reconcile any conflicting instructions@>;
@<Declare the format and wrapper as symbols@>;
HTMLUtilities::add_image_source(Pathnames::subfolder(Materials, I"images"));
HTMLUtilities::add_image_source(Pathnames::subfolder(path_to_indoc_materials, I"images"));
if (found_flag == FALSE)
Errors::fatal_with_text("unknown target %S", target_sought);
@ -201,13 +195,13 @@ applies 20 for all targets except |hypercard|, where it applies 40.
=
typedef struct ins_helper_state {
int found_aim;
struct indoc_instructions *settings;
struct settings_block *settings;
struct text_stream *desired_target;
struct text_stream *scanning_target;
} ins_helper_state;
int Instructions::read_instructions_from(filename *F, text_stream *desired,
indoc_instructions *settings) {
settings_block *settings) {
ins_helper_state ihs;
ihs.scanning_target = Str::new();
ihs.desired_target = desired;
@ -222,7 +216,7 @@ int Instructions::read_instructions_from(filename *F, text_stream *desired,
void Instructions::read_instructions_helper(text_stream *cl, text_file_position *tfp,
void *v_ihs) {
ins_helper_state *ihs = (ins_helper_state *) v_ihs;
indoc_instructions *settings = ihs->settings;
settings_block *settings = ihs->settings;
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, cl, L" *#%c*")) { Regexp::dispose_of(&mr); return; }
@ -269,9 +263,7 @@ void Instructions::read_instructions_helper(text_stream *cl, text_file_position
settings->book_contains_examples = TRUE;
} else if (Regexp::match(&mr, cl, L" *dc:(%C+): *(%c*?) *")) {
@<Disallow this in a specific target@>;
dc_metadatum *dcm = CREATE(dc_metadatum);
dcm->dc_key = Str::duplicate(mr.exp[0]);
dcm->dc_val = Str::duplicate(mr.exp[1]);
Instructions::create_ebook_metadata(Str::duplicate(mr.exp[0]), Str::duplicate(mr.exp[1]));
} else if (Regexp::match(&mr, cl, L" *css: *(%c*?) *")) {
@<Act on a CSS tweak@>;
} else if (Regexp::match(&mr, cl, L" *index: *(%c*?) *")) {
@ -397,15 +389,20 @@ taste). In a multiple-line value, each line is terminated with a newline.
@<Set an instructions option@> =
if (Str::eq_wide_string(key, L"alphabetization")) {
if (Str::eq_wide_string(val, L"word-by-word")) { settings->index_alphabetisation_algorithm = WORD_ALPHABETIZATION; }
else if (Str::eq_wide_string(val, L"letter-by-letter")) { settings->index_alphabetisation_algorithm = LETTER_ALPHABETIZATION; }
if (Str::eq_wide_string(val, L"word-by-word"))
settings->index_alphabetisation_algorithm = WORD_ALPHABETIZATION;
else if (Str::eq_wide_string(val, L"letter-by-letter"))
settings->index_alphabetisation_algorithm = LETTER_ALPHABETIZATION;
else Errors::in_text_file("no such alphabetization", tfp);
}
else if (Str::eq_wide_string(key, L"assume_Public_Library")) {
settings->assume_Public_Library = Instructions::set_yn(key, val, tfp); }
else if (Str::eq_wide_string(key, L"change_logs_directory")) { settings->change_logs_folder = Instructions::set_path(val, settings); }
else if (Str::eq_wide_string(key, L"contents_leafname")) { settings->contents_leafname = Str::duplicate(val); }
else if (Str::eq_wide_string(key, L"contents_expandable")) { settings->contents_expandable = Instructions::set_yn(key, val, tfp); }
else if (Str::eq_wide_string(key, L"assume_Public_Library"))
settings->assume_Public_Library = Instructions::set_yn(key, val, tfp);
else if (Str::eq_wide_string(key, L"change_logs_directory"))
settings->change_logs_folder = Instructions::set_path(val, settings);
else if (Str::eq_wide_string(key, L"contents_leafname"))
settings->contents_leafname = Str::duplicate(val);
else if (Str::eq_wide_string(key, L"contents_expandable"))
settings->contents_expandable = Instructions::set_yn(key, val, tfp);
else if (Str::eq_wide_string(key, L"css_source_file")) { settings->css_source_file = Instructions::set_file(val, settings); }
else if (Str::eq_wide_string(key, L"definitions_filename")) { settings->definitions_filename = Instructions::set_file(val, settings); }
else if (Str::eq_wide_string(key, L"definitions_index_filename")) {
@ -452,7 +449,7 @@ taste). In a multiple-line value, each line is terminated with a newline.
settings->link_to_extensions_index = Str::duplicate(val); }
else if (Str::eq_wide_string(key, L"manifest_leafname")) { settings->manifest_leafname = Str::duplicate(val); }
else if (Str::eq_wide_string(key, L"navigation")) {
settings->navigation = Gadgets::parse(val);
settings->navigation = Nav::parse(val);
if (settings->navigation == NULL) Errors::in_text_file("no such navigation mode", tfp);
}
else if (Str::eq_wide_string(key, L"retina_images")) {
@ -487,7 +484,7 @@ taste). In a multiple-line value, each line is terminated with a newline.
}
settings->contents_expandable = FALSE;
settings->images_copy = 1;
settings->navigation = Gadgets::for_ebook(settings->navigation);
settings->navigation = Nav::for_ebook(settings->navigation);
settings->format = HTML_FORMAT;
settings->XHTML = TRUE;
settings->ebook = Epub::new(I"untitled ebook", "");
@ -511,7 +508,7 @@ taste). In a multiple-line value, each line is terminated with a newline.
}
if (settings->format == PLAIN_FORMAT)
settings->navigation = Gadgets::for_plain_text(settings->navigation);
settings->navigation = Nav::for_plain_text(settings->navigation);
@<Declare the format and wrapper as symbols@> =
if (settings->wrapper == WRAPPER_epub) Symbols::declare_symbol(I"EPUB");
@ -526,7 +523,7 @@ Note the Unix-style conveniences for pathnames: an initial |~| means the
home folder, |~~| means the book folder.
=
pathname *Instructions::set_path(text_stream *val, indoc_instructions *settings) {
pathname *Instructions::set_path(text_stream *val, settings_block *settings) {
if (Str::get_at(val, 0) == '~') {
if (Str::get_at(val, 1) == '~') {
if ((Str::get_at(val, 2) == '/') || (Str::get_at(val, 2) == FOLDER_SEPARATOR)) {
@ -549,7 +546,7 @@ pathname *Instructions::set_path(text_stream *val, indoc_instructions *settings)
}
@ =
filename *Instructions::set_file(text_stream *val, indoc_instructions *settings) {
filename *Instructions::set_file(text_stream *val, settings_block *settings) {
if (Str::get_at(val, 0) == '~') {
if (Str::get_at(val, 1) == '~') {
if ((Str::get_at(val, 2) == '/') || (Str::get_at(val, 2) == FOLDER_SEPARATOR)) {
@ -605,6 +602,18 @@ int Instructions::set_yn(text_stream *key, text_stream *val, text_file_position
@ For ebooks only.
=
typedef struct dc_metadatum {
struct text_stream *dc_key;
struct text_stream *dc_val;
MEMORY_MANAGEMENT
} dc_metadatum;
void Instructions::create_ebook_metadata(text_stream *key, text_stream *value) {
dc_metadatum *dcm = CREATE(dc_metadatum);
dcm->dc_key = Str::duplicate(key);
dcm->dc_val = Str::duplicate(value);
}
void Instructions::apply_ebook_metadata(ebook *E) {
dc_metadatum *dcm;
LOOP_OVER(dcm, dc_metadatum) {

View file

@ -2,39 +2,15 @@
The top level of the program.
@h Definitions.
@ Indoc is one of the earliest Inform tools, and it spent much of its
life as a hacky Perl script: like all too many quick-fix Perl scripts, it
was still in use ten years later. In 2012, I spent some time tidying it up
to generate better HTML, and moved it over to literate code. This took an
exasperatingly long time, not least because the original had produced
typically sloppy turn-of-the-century HTML, with tables for layout and
no CSS, and with many now-deprecated tags and elements. The 2012 edition,
by contrast, needed to produce validatable XHTML 1.1 Strict in order to
make EPUBs which read roughly correctly in today's ebook-readers, and
when they call this Strict they're not kidding. It took something like
four weeks of spare evenings.
Just as I was finishing up, the programmer and commentator John Siracusa
described an almost identical web-content-and-ebook generation task on his
podcast (Hypercritical 85): "My solution for this is... I was trying to
think of a good analogy for what happens when you're a programmer and you
have this sort of task in front of you. Is it, the cobbler's children have
no shoes? ... You would expect someone who is a programmer to make some
awesome system which would generate these three things. But when you're a
programmer, you have the ability to do whatever you want really, really
quickly in the crappiest possible way... And that's what I did. I wrote a
series of incredibly disgusting Perl scripts."
This made me feel better. Nevertheless, in 2016, I rewrote in C.
@h Nutshell.
We turn the source matter, "rawtext", into a batch of output files using the
chosen format, a process we'll call "rendering". We do this in two passes.
=
indoc_instructions *indoc_settings = NULL;
pathname *path_to_indoc = NULL; /* where we are installed */
pathname *path_to_indoc_materials = NULL; /* the materials pathname */
settings_block *indoc_settings = NULL;
int no_volumes = 0;
int no_examples = 0;
@ -61,10 +37,13 @@ int main(int argc, char **argv) {
@<Start up indoc@> =
PRINT("indoc [[Version Number]] (Inform Tools Suite)\n");
Gadgets::start();
Nav::start();
Symbols::start_up_symbols();
indoc_settings = Instructions::clean_slate();
Configuration::read_command_line(argc, argv, indoc_settings);
path_to_indoc = Pathnames::installation_path("INDOC_PATH", I"indoc");
if (indoc_settings->verbose_mode) PRINT("Installation path is %p\n", path_to_indoc);
path_to_indoc_materials = Pathnames::subfolder(path_to_indoc, I"Materials");
if (indoc_settings->wrapper == WRAPPER_epub) {
HTMLUtilities::image_URL(NULL,
Filenames::get_leafname(indoc_settings->book_cover_image));
@ -97,7 +76,7 @@ section by section.
volume *V;
text_stream *TO = NULL;
LOOP_OVER(V, volume) TO = Rawtext::process_large_rawtext_file(TO, V);
Gadgets::render_navigation_contents_files();
Nav::render_navigation_contents_files();
@ The following functions here are for use only when compiling documentation
to go inside the Inform user interface application.

View file

@ -213,7 +213,7 @@ all CSS files, but here we know that |base.css| is tidily written.
@ =
void CSS::expand_IMAGES(text_stream *text) {
match_results mr = Regexp::create_mr();
if (indoc_settings->suppress_fonts == 1) {
if (indoc_settings->suppress_fonts) {
while (Regexp::match(&mr, text, L"(%c*?)font-family:(%c*?);(%c*)")) {
text_stream *L = mr.exp[0], *M = NULL, *R = mr.exp[2];
if (Regexp::match(NULL, mr.exp[1], L"%c*monospace%c*")) M = I"___MONOSPACE___";

View file

@ -85,11 +85,11 @@ routine which doesn't surround the text with navigational gadgets and headings.
text_stream *Renderer::render_block(OUTPUT_STREAM, volume *V, section *S) {
OUT = Renderer::formatted_file_must_be(OUT, V, S);
Gadgets::render_navigation_top(OUT, V, S);
Nav::render_navigation_top(OUT, V, S);
Renderer::render_text_of_block(OUT, V, S);
Gadgets::render_navigation_middle(OUT, V, S);
Nav::render_navigation_middle(OUT, V, S);
@<Render the examples below the text of the block@>;
Gadgets::render_navigation_bottom(OUT, V, S);
Nav::render_navigation_bottom(OUT, V, S);
return OUT;
}
@ -102,14 +102,14 @@ text_stream *Renderer::render_block(OUTPUT_STREAM, volume *V, section *S) {
if (E->example_displayed_at_section[V->allocation_id] == S) {
no_examples_rendered_here++;
if (no_examples_rendered_here == 1)
Gadgets::render_navigation_example_top(OUT, V, S);
Nav::render_navigation_example_top(OUT, V, S);
@<Render the example here@>;
if (indoc_settings->examples_mode == EXMODE_open_internal) HTMLUtilities::ruled_line(OUT);
}
}
if (no_examples_rendered_here > 0)
Gadgets::render_navigation_example_bottom(OUT, V, S);
Nav::render_navigation_example_bottom(OUT, V, S);
@ Examples need to connect with particular sections of documentation, but
they do so by title, not by block number, to protect them from renumbering

View file

@ -35,7 +35,7 @@ text_stream *IndexUtilities::open_page(text_stream *title, text_stream *leafname
}
DISCARD_TEXT(head);
Gadgets::render_navigation_index_top(OUT, leafname, title);
Nav::render_navigation_index_top(OUT, leafname, title);
return OUT;
}

View file

@ -8,7 +8,7 @@ heading includes these anyway.
=
navigation_design *Architect::create(void) {
navigation_design *ND = Gadgets::new(I"architect", FALSE, FALSE);
navigation_design *ND = Nav::new(I"architect", FALSE, FALSE);
ND->columnar = TRUE;
ND->contents_body_class = I"paper architectpapertint";
METHOD_ADD(ND, RENDER_SECTION_TITLE_MTID, Architect::architect_section_title);

View file

@ -7,7 +7,7 @@ no navigational features at all.
=
navigation_design *Lacuna::create(void) {
navigation_design *ND = Gadgets::new(I"lacuna", FALSE, TRUE);
navigation_design *ND = Nav::new(I"lacuna", FALSE, TRUE);
METHOD_ADD(ND, RENDER_CHAPTER_TITLE_MTID, Lacuna::lacuna_chapter_title);
METHOD_ADD(ND, RENDER_SECTION_TITLE_MTID, Lacuna::lacuna_section_title);
return ND;

View file

@ -6,7 +6,7 @@ The "midnight" style of navigational gadgets.
=
navigation_design *Midnight::create(void) {
navigation_design *ND = Gadgets::new(I"midnight", FALSE, FALSE);
navigation_design *ND = Nav::new(I"midnight", FALSE, FALSE);
ND->columnar = TRUE;
METHOD_ADD(ND, RENDER_SECTION_TITLE_MTID, Midnight::midnight_section_title);
METHOD_ADD(ND, RENDER_INDEX_TOP_MTID, Midnight::midnight_navigation_index_top);
@ -197,7 +197,7 @@ void Midnight::write_contents_page(navigation_design *self, volume *V) {
TEMPORARY_TEXT(title);
WRITE_TO(title, "Contents");
@<Begin the HTML page for the contents@>;
Gadgets::navigation_contents_heading(OUT, V);
Nav::navigation_contents_heading(OUT, V);
for (int column = 0; column < no_volumes; column++)
if ((column == V->allocation_id) || (self->columnar))

View file

@ -6,7 +6,7 @@ The "roadsign" style of navigational gadgets.
=
navigation_design *Roadsign::create(void) {
navigation_design *ND = Gadgets::new(I"roadsign", TRUE, FALSE);
navigation_design *ND = Nav::new(I"roadsign", TRUE, FALSE);
METHOD_ADD(ND, RENDER_VOLUME_TITLE_MTID, Roadsign::roadsign_volume_title);
METHOD_ADD(ND, RENDER_CHAPTER_TITLE_MTID, Roadsign::roadsign_chapter_title);
METHOD_ADD(ND, RENDER_SECTION_TITLE_MTID, Roadsign::roadsign_section_title);

View file

@ -7,7 +7,7 @@ of "midnight".
=
navigation_design *Twilight::create(void) {
navigation_design *ND = Gadgets::new(I"twilight", FALSE, FALSE);
navigation_design *ND = Nav::new(I"twilight", FALSE, FALSE);
ND->simplified_examples = TRUE;
ND->simplified_letter_rows = TRUE;
METHOD_ADD(ND, RENDER_CHAPTER_TITLE_MTID, Twilight::twilight_chapter_title);

View file

@ -6,7 +6,7 @@ The "unsigned" style of navigational gadgets.
=
navigation_design *Unsigned::create(void) {
navigation_design *ND = Gadgets::new(I"unsigned", TRUE, FALSE);
navigation_design *ND = Nav::new(I"unsigned", TRUE, FALSE);
METHOD_ADD(ND, RENDER_VOLUME_TITLE_MTID, Unsigned::unsigned_volume_title);
METHOD_ADD(ND, RENDER_CHAPTER_TITLE_MTID, Unsigned::unsigned_chapter_title);
METHOD_ADD(ND, RENDER_SECTION_TITLE_MTID, Unsigned::unsigned_section_title);

View file

@ -1,221 +0,0 @@
[Gadgets::] Navigational Gadgets.
To render linking gadgets in HTML forms of documentation, so that
the reader can navigate from section to section.
@h
=
typedef struct navigation_design {
struct text_stream *codename;
int ebook_friendly;
int plain_friendly;
int columnar;
int simplified_examples;
int simplified_letter_rows;
struct text_stream *contents_body_class;
METHOD_CALLS
MEMORY_MANAGEMENT
} navigation_design;
navigation_design *Gadgets::new(text_stream *code, int e, int p) {
navigation_design *ND = CREATE(navigation_design);
ND->codename = Str::duplicate(code);
ND->ebook_friendly = e;
ND->plain_friendly = p;
ND->columnar = FALSE;
ND->simplified_examples = FALSE;
ND->simplified_letter_rows = FALSE;
ND->contents_body_class = I"paper midnightpapertint";
ND->methods = Methods::new_set();
return ND;
}
void Gadgets::start(void) {
Midnight::create(); /* needs to be created first */
Twilight::create();
Architect::create();
Roadsign::create(); /* needs to be created before unsigned */
Unsigned::create();
Lacuna::create();
}
navigation_design *Gadgets::default(void) {
return FIRST_OBJECT(navigation_design);
}
navigation_design *Gadgets::for_ebook(navigation_design *current) {
if (current->ebook_friendly) return current;
navigation_design *ND;
LOOP_OVER(ND, navigation_design)
if (ND->ebook_friendly)
return ND;
return NULL;
}
navigation_design *Gadgets::for_plain_text(navigation_design *current) {
if (current->plain_friendly) return current;
navigation_design *ND;
LOOP_OVER(ND, navigation_design)
if (ND->plain_friendly)
return ND;
return NULL;
}
navigation_design *Gadgets::parse(text_stream *val) {
navigation_design *ND;
LOOP_OVER(ND, navigation_design)
if (Str::eq(val, ND->codename))
return ND;
return NULL;
}
@h Top.
At the front end of a section, before any of its text.
@e RENDER_VOLUME_TITLE_MTID
@e RENDER_CHAPTER_TITLE_MTID
@e RENDER_SECTION_TITLE_MTID
=
VMETHOD_TYPE(RENDER_VOLUME_TITLE_MTID, navigation_design *ND, text_stream *OUT, volume *V)
VMETHOD_TYPE(RENDER_CHAPTER_TITLE_MTID, navigation_design *ND, text_stream *OUT, volume *V, chapter *C)
VMETHOD_TYPE(RENDER_SECTION_TITLE_MTID, navigation_design *ND, text_stream *OUT, volume *V, chapter *C, section *S)
void Gadgets::render_navigation_top(OUTPUT_STREAM, volume *V, section *S) {
if (V->sections[0] == S) VMETHOD_CALL(indoc_settings->navigation, RENDER_VOLUME_TITLE_MTID, OUT, V);
chapter *C = S->begins_which_chapter;
if (C) VMETHOD_CALL(indoc_settings->navigation, RENDER_CHAPTER_TITLE_MTID, OUT, V, C);
if (indoc_settings->html_for_Inform_application)
@<Write HTML comments giving the Inform user interface search assistance@>;
VMETHOD_CALL(indoc_settings->navigation, RENDER_SECTION_TITLE_MTID, OUT, V, C, S);
}
@<Write HTML comments giving the Inform user interface search assistance@> =
WRITE("\n");
TEMPORARY_TEXT(comment);
WRITE_TO(comment, "SEARCH TITLE \"%S\"", S->unlabelled_title);
HTML::comment(OUT, comment);
Str::clear(comment);
WRITE_TO(comment, "SEARCH SECTION \"%S\"", S->label);
HTML::comment(OUT, comment);
Str::clear(comment);
WRITE_TO(comment, "SEARCH SORT \"%S\"", S->sort_code);
HTML::comment(OUT, comment);
DISCARD_TEXT(comment);
@h Index top.
And this is a variant for index pages, such as the index of examples.
@e RENDER_INDEX_TOP_MTID
=
VMETHOD_TYPE(RENDER_INDEX_TOP_MTID, navigation_design *ND, text_stream *OUT, text_stream *filename, text_stream *title)
void Gadgets::render_navigation_index_top(OUTPUT_STREAM, text_stream *filename, text_stream *title) {
VMETHOD_CALL(indoc_settings->navigation, RENDER_INDEX_TOP_MTID, OUT, filename, title);
}
@h Middle.
At the middle part, when the text is over, but before any example cues.
@e RENDER_NAV_MIDDLE_MTID
=
VMETHOD_TYPE(RENDER_NAV_MIDDLE_MTID, navigation_design *ND, text_stream *OUT, volume *V, section *S)
void Gadgets::render_navigation_middle(OUTPUT_STREAM, volume *V, section *S) {
VMETHOD_CALL(indoc_settings->navigation, RENDER_NAV_MIDDLE_MTID, OUT, V, S);
}
@h Example top.
This is reached before the first example is rendered, provided at least
one example will be:
@e RENDER_EXAMPLE_TOP_MTID
=
VMETHOD_TYPE(RENDER_EXAMPLE_TOP_MTID, navigation_design *ND, text_stream *OUT, volume *V, section *S)
void Gadgets::render_navigation_example_top(OUTPUT_STREAM, volume *V, section *S) {
if (indoc_settings->format == HTML_FORMAT) {
HTML::begin_div_with_class_S(OUT, I"bookexamples");
HTML_OPEN_WITH("p", "class=\"chapterheading\"");
}
if (indoc_settings->examples_granularity == CHAPTER_GRANULARITY) {
chapter *C = S->in_which_chapter;
WRITE("Examples from %S", C->chapter_full_title);
} else if (indoc_settings->examples_granularity == BOOK_GRANULARITY) {
WRITE("Examples");
}
if (indoc_settings->format == HTML_FORMAT) {
HTML_CLOSE("p");
} else { WRITE("\n\n"); }
VMETHOD_CALL(indoc_settings->navigation, RENDER_EXAMPLE_TOP_MTID, OUT, V, S);
}
@h Example bottom.
Any closing ornament at the end of examples? This is reached after the
last example is rendered, provided at least one example has been.
@e RENDER_EXAMPLE_BOTTOM_MTID
=
VMETHOD_TYPE(RENDER_EXAMPLE_BOTTOM_MTID, navigation_design *ND, text_stream *OUT, volume *V, section *S)
void Gadgets::render_navigation_example_bottom(OUTPUT_STREAM, volume *V, section *S) {
if (indoc_settings->format == PLAIN_FORMAT) {
WRITE("\n\n");
}
if (indoc_settings->format == HTML_FORMAT) {
if (indoc_settings->examples_mode != EXMODE_open_internal) { HTMLUtilities::ruled_line(OUT); }
HTML::end_div(OUT);
}
VMETHOD_CALL(indoc_settings->navigation, RENDER_EXAMPLE_BOTTOM_MTID, OUT, V, S);
}
@h Bottom.
At the end of the section, after any example cues and perhaps also example
bodied. (In a section with no examples, this immediately follows the middle.)
@e RENDER_NAV_BOTTOM_MTID
=
VMETHOD_TYPE(RENDER_NAV_BOTTOM_MTID, navigation_design *ND, text_stream *OUT, volume *V, section *S)
void Gadgets::render_navigation_bottom(OUTPUT_STREAM, volume *V, section *S) {
if (indoc_settings->format == HTML_FORMAT) {
HTML::comment(OUT, I"START IGNORE");
}
VMETHOD_CALL(indoc_settings->navigation, RENDER_NAV_BOTTOM_MTID, OUT, V, S);
if (indoc_settings->format == HTML_FORMAT) {
HTML::comment(OUT, I"END IGNORE");
}
}
@h Contents page.
Midnight provides a contents page of its very own.
@e RENDER_CONTENTS_MTID
@e RENDER_CONTENTS_HEADING_MTID
=
VMETHOD_TYPE(RENDER_CONTENTS_MTID, navigation_design *ND)
VMETHOD_TYPE(RENDER_CONTENTS_HEADING_MTID, navigation_design *ND, text_stream *OUT, volume *V)
void Gadgets::render_navigation_contents_files(void) {
VMETHOD_CALLV(indoc_settings->navigation, RENDER_CONTENTS_MTID);
}
void Gadgets::navigation_contents_heading(OUTPUT_STREAM, volume *V) {
VMETHOD_CALL(indoc_settings->navigation, RENDER_CONTENTS_HEADING_MTID, OUT, V);
}

View file

@ -12,11 +12,16 @@ Build Date: 16 February 2019
Import: foundation
Preliminaries
Introduction to Indoc
Volumes and Instructions
Documentation Markup
Chapter 1: Setting Up
Basics
Instructions
Configuration
Main
Configuration
Instructions
Context Symbols
Chapter 2: Processing
@ -34,7 +39,7 @@ Chapter 3: Indexing
Examples Index
Chapter 4: Navigation Styles
Navigational Gadgets
Navigational Designs
Codename Lacuna
Codename Midnight
Codename Architect

View file

@ -0,0 +1,5 @@
Documentation Markup.
How to mark up the plain-text source for Inform documentation.
@h Stuff.

View file

@ -0,0 +1,93 @@
Introduction to Indoc.
What Indoc is, and its limited but complicated uses.
@ Intest is a command line tool for generating (mainly) HTML or EPUB format
documentation. A million of those have been written, and Indoc has no
ambition to replace them. It is needed because Inform 7's documentation
source consists of many small text files with idiosyncratic markup, while
its formatted HTML version needs to be indexed in elaborate ways.
Indoc is a purely command-line tool, used in building Inform but not in
running it: it's not present in the Inform UI apps.
If you have compiled the standard distribution of the command-line tools
for Inform then the Indoc executable will be at |indoc/Tangled/indoc/|.
Usage is very simple:
|$ indoc/Tangled/indoc [OPTIONS] TARGET|
By default, Indoc reads its source documentation from a direction called
|Documentation| (with respect to the current working directory); the
option |-from X| changes this path to |X|, but in this manual we'll call
it |Documentation|.
In addition to documentation files, which will be described later, Indoc
also reads instruction files. At minimum it will read
|Documentation/indoc-instructions.txt|
but the option |-instructions X| causes it to read |X| as well. Instructions
files mainly specify indexing notations, or CSS styles, or miscellaneous
settings, but they group these under named "targets". For example:
|windows_app {|
| ...|
|}|
declares a target called |windows_app|. (This is the form of HTML needed for
use inside the Windows UI application for Inform.) The idea here is that
there is probably no single form of HTML needed -- it will be needed in
subtly different versions for different platforms: inside the app, as a
stand-alone website, inside an Epub ebook. These different forms are
called "targets". On any given run, Indoc generates a single target --
the one named on the command line.
The HTML produced is placed, by default, in the directory:
|Documentation/Output|
This can be changed with the option |-to X|.
@ When it runs, Indoc needs to know where it is installed in the file
system. There is no completely foolproof, cross-platform way to know this
(on some Unixes, a program cannot determine its own location), so Indoc
decides by the following set of rules:
(a) If the user, at the command line, specified |-at P|, for some path
|P|, then we use that.
(b) Otherwise, if the host operating system can indeed tell us where the
executable is, we use that. This is currently implemented only on MacOS,
Windows and Linux.
(c) Otherwise, if the environment variable |$INDOC_PATH| exists and is
non-empty, we use that.
(d) And if all else fails, we assume that the location is |indoc|, with
respect to the current working directory.
If you're not sure what Indoc has decided and suspect it may be wrong,
running Indoc with the |-verbose| switch will cause it to print its belief
about its location as it starts up.
@ As a program, Indoc began as a rat's nest of Perl in 2002, and you can still
see where the rats used to live. Like all too many quick-fix Perl scripts, it
was still in use ten years later. In 2012, I spent some time tidying it up to
generate better HTML, and made it a web (that is, a literate program). The
original had produced typically sloppy turn-of-the-century HTML, with tables
for layout and no CSS, and with many now-deprecated tags and elements. The
2012 edition, by contrast, needed to produce validatable XHTML 1.1 Strict in
order to make EPUBs which read roughly correctly in today's ebook-readers, and
when they call this Strict they're not kidding. It took something like four
weeks of spare evenings.
Just as I was finishing up, the programmer and commentator John Siracusa
described a not dissimilar web-content-and-ebook generation task on his
podcast (Hypercritical 85): "I was trying to think of a good analogy for what
happens when you're a programmer and you have this sort of task in front of
you. Is it, the cobbler's children have no shoes? ... You would expect someone
who is a programmer to make some awesome system which would generate these
three things. But when you're a programmer, you have the ability to do
whatever you want really, really quickly in the crappiest possible way... And
that's what I did. I wrote a series of incredibly disgusting Perl scripts."
This made me feel better. Nevertheless, in 2016, indoc was rewritten in C,
and it received a further revision in 2019.

View file

@ -0,0 +1,321 @@
Volumes and Instructions.
Dual- versus single-volume mode, and how to write instructions files.
@h Model.
Conceptually, an Indoc project has either one or two volumes. The source for
each volume is a single UTF-8 encoded plain text file. In the core Inform
repository, there are two volumes, with the files being
|Documentation/Writing with Inform.txt|
|Documentation/The Recipe Book.txt|
These are independent books, with individual titles. It would seem simpler
just to make them two different Indoc projects, but in dual-volume mode,
Indoc can generate joint contents pages, and provide crosswise HTML links
between the two volumes.
The project can also include a number of "Examples", each being a single
text file such as:
|Documentation/Examples/Prague.txt|
which is the source for an Inform example called "The Prague Job".
(These same text files are also used by Intest to test that all of the code
samples included in the Inform documentation actually work as claimed.)
There can be any number of examples, including none; Inform currently has 468.
Each volume is divided into a series of chapters, and each chapter into a
series of sections. Examples are always placed at the ends of sections;
note that in dual-volume mode, examples are (mostly) present in both volumes,
giving them two different locations. Thus, "The Prague Job" appears in section
"More general linkages" of chapter "Scenes" of volume "Writing with Inform",
and also in section "Scripted Scenes" of chapter "Time and Plot" of volume
"The Recipe Book".
@h Project instructions.
The main instructions file for an Indoc project is, as noted earlier, at:
|Documentation/indoc-instructions.txt|
An instruction file is a UTF-8 encoded plain text file. Single instructions
occupy single lines (i.e., line breaks are significant). A white-space line,
or a line whose first non-white-space character is a |#|, are ignored.
The file should begin by specifying one or two volumes, and then, if they
will contain Examples in the above sense, by giving the special |examples|
instruction. Inform opens thus:
|volume: Writing with Inform|
|volume: The Inform Recipe Book (RB) = The Recipe Book.txt|
|examples|
But a simpler, single-volume project might have just:
|volume: Pandemonium 2.0 for Fun and Profit|
Each volume has a title, and Indoc automatically generates an abbreviation
for it: by default, it takes the capital letters from the title, so that it
abbreviates "Writing with Inform" to WI. That same method would have made
turned "The Inform Recipe Book" into TIRB, but because we didn't want that,
we supplied our own abbreviation RB instead.
The third, also optional, part of a |volume| instruction specifies the
leafname of the documentation source file for it. By default, this will be
the title plus |.txt|: for example, |Writing with Inform.txt|. But we can
use |= X| to specify that it should be |X| instead.
Two other project instructions exist:
If the project will contain images, then they will be looked for in a list
of places. Top of the list is a directory internal to Indoc which includes
some navigation icons such as |arrow-up.png|. The instruction |images: X|
adds the directory |X| to this source list.
Lastly, the cover image for the project can be specified with an instruction
such as:
|cover: combined_cover.png|
This specifies a leafname which must exist in one of the image sources
mentioned above.
@h Durham Core metadata.
If the project needs to generate Epub books, then these will need to have
some basic DC ("Durham Core") metadata supplied. For example:
|dc:title: Inform - A Design System for Interactive Fiction|
|dc:creator: Graham Nelson and Emily Short|
|dc:subject: Interactive Fiction|
|dc:identifier: wwi-rb-combined|
The instruction |dc:KEY: VALUE| supplies a DC key-value pair.
@h Targets.
The instructions file typically begins as above, but then goes into a
block of general settings or instructions (for which see below); and
eventually gets around to describing one or more targets. A target
looks like so:
|IDENTIFIER {|
| ...|
|}|
where |IDENTIFIER| is its name. Targets, as noted in the introduction,
are different forms of the documentation we might need to produce: Inform,
for example, has targets called |plain|, |website|, |linux_app| and so on.
What's important here is not that these are written to different locations
on disc (though they are) but that they have finicky little differences
in settings. The |...| stretch of lines can specify these. For example:
|ebook {|
| granularity = 2|
| examples_mode = open|
| follow: epub-css-tweaks.txt|
|}|
makes two specific settings and one instruction, all applying only for the
target |ebook|.
@h Symbols.
The instruction |declare: SYMBOL| creates the symbol |SYMBOL|. These exist
so that we can mark certain paragraphs of documentation as being present in
only some of the targets.
For example, we might want Linux installation instructions to appear only
in the Linux version of a manual. To do that, we'll need the symbol:
|linux_app {|
| ...|
| declare: Linux|
| ...|
|}|
In the documentation, we could then mark up a paragraph like so:
|{Linux:}To install, first...|
The symbol |indoc| is always declared, but by default no other symbols are.
Lastly, |undeclare: SYMBOL| removes a symbol.
@h Other instructions.
|follow: I| tells Indoc to follow the instructions file |I|. This works
rather like |#include| in C, or similar languages. If the |follow:| is
included inside a target block, then it affects only that target. On
other targets, the file |I| won't even be opened, and need never exist.
|css:| specifies additional CSS (Cascading Style Sheet) styling. This
will be needed only if, for example, unusual indexing features are used,
in which different categories of index entry need different visual styling.
For example,
|css: span.indextitle ++ {|
| font-style: italic;|
|}|
Here the material between the braces is pure CSS, not Indoc syntax. The
notation |++| here tells Indoc that an entirely new CSS style is being
created; |+| would supply new lines to an existing style.
|index: NOTATION = CATEGORY OPTION| defines a new indexing markup notation;
for example,
|index: ^{@headword} = name (invert)|
says that markup notations like |^{@Andrew Plotkin}| put a name into the index,
which should be an index entry of category |name|, and should be inverted,
in that it will be alphabetised under "Plotkin, Andrew". The text |headword|
in the prototype is where the entry text should appear in the notation.
@h Miscellaneous settings.
There are a great many of these, but most are set to sensible defaults,
and it is not compulsory to set any of them. Lines such as
|SETTING = VALUE|
change the default settings if need be. Here is an A-Z list; they're really
too miscellaneous to be grouped usefully by subject matter.
|alphabetization| sets the index sorting algorithm. The default is
|letter-by-letter|; the alternative is |word-by-word|. The difference is
that letter-by-letter would ignore word divisions and sort in the order
"peach", "peachpit", "peach tree"; whereas word-by-word would go for
"peach", "peach tree", "peachpit".
|assume_Public_Library| can be |yes| or |no|. The default is |no|. This
specifies whether special HTML links to the Public Library will be valid;
outside of Inform UI apps, the answer is definitely no.
|change_logs_directory| is the path to a directory holding Inform release
change log files. By default, this will be |Documentation/Change Logs|.
|contents_leafname| is the (unextended) leafname to give the HTML contents
page. The default is |index|.
|contents_expandable| can be |yes| or |no|. The default is |no|. This sets
whether Javascript-powered "expand" buttons are to be used in the contents
page, and has effect only on the Midnight navigation design.
|css_source_file| is the filename of the CSS style sheet to use. The default
is the |base.css| file included in the Indoc distribution.
|definitions_filename| is the filename to use if you would like Indoc to
output a special file of Inform phrase definitions, for use by Inform itself
when it generates indexes. The default for this is |definitions.html|. This
has nothing to do with the |definitions_index_filename|.
|definitions_index_filename| is the leafname to use for the General Index
in the documentation. The default is |general_index.html|. This
has nothing to do with the |definitions_filename|.
|destination| is the directory into which output is generated. The default
is |Documentation/Output|. Note that specifying |-to X| at the command line
overrides this setting: if |-to| is used, |destination| is ignored.
|examples_directory| is the directory holding the Example files. The default
is |Documentation/Examples|.
|examples_alphabetical_leafname| is the leafname to use for the alphabetical
index of examples in the documentation. The default is |examples_alphabetical.html|.
|examples_granularity| is 1, 2, or 3. It can never be less than |granularity|,
and by default is equal to it. It specifies where examples should appear:
at the end of the relevant volume (1), chapter (2), or section (3).
|examples_mode| is |open| or |openable|, and is by default |open|. Open means
that an example has its full contents visible by default; openable means that
the contents are hidden behind a Javascript-powered button which causes them
to be revealed.
|examples_numerical_leafname| is the leafname to use for the numerical
index of examples in the documentation. The default is |examples_numerical.html|.
|examples_thematic_leafname| is the leafname to use for the thematic
index of examples in the documentation. The default is |examples_thematic.html|.
|format| is the most important of all the settings, and is |HTML| or |text|,
but by default |HTML| unless the target name is |plain|, in which case |text|.
|granularity| is 1, 2, or 3. The default is 3 unless the target is called
|webpage| or |plain|, in which case it is 1. This specifies how much the
documentation is broken down into pieces. 1 means "each volume in a single
HTML file"; 2 means "each chapter", 3 means "each section". Low granularity
means fewer but larger files, high granularity more but smaller files.
|html_for_Inform_application| can be |yes| or |no|. The default is |no|. This
specifies whether the HTML is for use inside the Inform UI application, and
can therefore use links with the special HTTP transports only available there.
|images_copy| can be |yes| or |no|. The default is |yes|. In this mode,
any needed image files are copied into place into the |images_path|. (The
alternative assumes they are already there, and should be used if |images_path|
is some URL external to the HTML being generated.)
|images_path| is where the generated HTML expects to find its image files.
The default is |~~/Images/|, where |~~| means the destination directory:
that is, the default is a subdirectory called |Images| of the destination.
|inform_definitions_mode| can be |yes| or |no|. The default is |no|. This
is cosmetic, and provides extra styling on lines of documentation giving the
syntax for Inform phrases.
|javascript| can be |yes| or |no|. The default is |yes|. This indicates
whetber Indoc is allowed to compile Javascript, or has to stick to inactive
HTML.
|javascript_paste_method| can be |none|, |Andrew| or |David|. The default
is |none|. The difference relates to how "paste Inform source" links are
implemented inside the Inform application: |Andrew| mode is suitable for
most platforms, but |David| is needed for Windows.
|link_to_extensions_index| is meaningful only if |html_for_Inform_application|
is set, and specifies the URL of the Extensions index inside the app.
|manifest_leafname| is meaningful only if |html_for_Inform_application|
is set, and is by default |manifest.txt|. This provides a cross-reference
list of files generated by Indoc.
|navigation| is the design used for navigation links in the HTML produced.
There are currently six designs, called |architect|, |lacuna|, |midnight|,
|roadsign|, |twilight|, and |unsigned|; the default is |roadsign|, though
inside the Inform applications, the design chosen is usually |architect|.
If the format is |text| not |HTML|, then the design is always |lacuna|.
|retina_images| can be |yes| or |no|. The default is |no|. This indicates
whether MacOS/iOS "retina" versions of the paste and create icons are
available: |paste@2x.png| and |create@2x.png| respectively.
|support_creation| can be |yes| or |no|. The default is |no|. This indicates
whether the Examples have a "create" button which creates a new Inform
project demonstrating them in action; this can only be done in the UI apps,
so it should always be |no| unless |html_for_Inform_application| is |yes|.
|suppress_fonts| can be |yes| or |no|. The default is |no|. If |yes|, this
strips out lists of fonts to use in CSS, leaving only whether they are
|monospace| or not.
|toc_granularity| is 1, 2, or 3. It can never be less than |granularity|,
and by default is 3. It shows the level of detail in the table of contents: 1
means volumes, 2 means volumes and chapters, 3 goes down to sections.
|top_and_tail| specifies a prototype HTML file to follow for the more
important HTML pages generated by Indoc. The default is not to. If
this exists, it can provide a surround for the HTML we generate -- for
example, it can contain website-specific navigation, or a banner across
the top. The prototype should somewhere include the text |[TEXT]|, and
this will be replaced with whatever Indoc generates.
|top_and_tail_sections| is the same as |top_and_tail|, but for individual
section files.
|treat_code_as_verbatim| can be |yes| or |no|. The default is |yes|. This
affects the styling of marked-up code material in documentation. Without
it, code markup is largely unavailable.
|wrapper| can be |EPUB|, |zip| or |none|. The default is |none|. The wrapper
is put around the whole mass of generated HTML; |EPUB| makes the result an
Epub-format ebook.
|XHTML| can be |yes| or |no|. The default is |no|. This forces the HTML we
produce to conform to XHTML 1.1 Strict. If the |wrapper| is |EPUB|, then
this is automatically set to |yes|.