mirror of
https://github.com/ganelson/inform.git
synced 2024-06-29 05:24:57 +03:00
Moved census mode out of core Inform
This commit is contained in:
parent
7cc9c598f3
commit
9206d88dbb
|
@ -22,7 +22,7 @@
|
|||
set: $I6OPTIONS = -kE2~S~Dwv8
|
||||
set: $BLORBEXT = zblorb
|
||||
endif
|
||||
set: $I7OPTIONS = -fixtime -release -format=$FORMAT -noprogress -rng -sigils -clock -log nothing -external inblorb/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7OPTIONS = -fixtime -release -format=$FORMAT -noprogress -rng -sigils -log nothing -external inblorb/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $CBLORBOPTIONS = -fixtime
|
||||
|
||||
mkdir: $WORK/Transient
|
||||
|
|
|
@ -28,7 +28,7 @@ linked_list *inbuild_nest_list = NULL;
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
Foundation::start();
|
||||
WordsModule::start();
|
||||
HTMLModule::start();
|
||||
ArchModule::start();
|
||||
InbuildModule::start();
|
||||
targets = NEW_LINKED_LIST(inbuild_copy);
|
||||
|
@ -74,6 +74,7 @@ int main(int argc, char **argv) {
|
|||
WordsModule::end();
|
||||
ArchModule::end();
|
||||
InbuildModule::end();
|
||||
HTMLModule::end();
|
||||
Foundation::end();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ Version Name: Avignon
|
|||
|
||||
Import: foundation
|
||||
Import: inform7/words
|
||||
Import: html
|
||||
Import: arch
|
||||
Import: inbuild
|
||||
|
||||
|
|
41
inbuild/html-module/Chapter 1/HTML Module.w
Normal file
41
inbuild/html-module/Chapter 1/HTML Module.w
Normal file
|
@ -0,0 +1,41 @@
|
|||
[HTMLModule::] Index Module.
|
||||
|
||||
Setting up the use of this module.
|
||||
|
||||
@h Introduction.
|
||||
|
||||
@d HTML_MODULE TRUE
|
||||
|
||||
@ To begin with, this module needs to allocate memory:
|
||||
|
||||
=
|
||||
|
||||
@h The beginning.
|
||||
(The client doesn't need to call the start and end routines, because the
|
||||
foundation module does that automatically.)
|
||||
|
||||
=
|
||||
void HTMLModule::start(void) {
|
||||
@<Register this module's stream writers@>;
|
||||
@<Register this module's debugging log aspects@>;
|
||||
@<Register this module's debugging log writers@>;
|
||||
@<Register this module's command line switches@>;
|
||||
}
|
||||
|
||||
@<Register this module's stream writers@> =
|
||||
;
|
||||
|
||||
@<Register this module's debugging log aspects@> =
|
||||
;
|
||||
|
||||
@<Register this module's debugging log writers@> =
|
||||
;
|
||||
|
||||
@<Register this module's command line switches@> =
|
||||
;
|
||||
|
||||
@h The end.
|
||||
|
||||
=
|
||||
void HTMLModule::end(void) {
|
||||
}
|
|
@ -235,6 +235,12 @@ far as the user is concerned it opens the example and goes there.
|
|||
HTML_CLOSE("a");
|
||||
HTML_TAG("br");
|
||||
|
||||
@
|
||||
|
||||
=
|
||||
<table-sentence> ::=
|
||||
<if-start-of-paragraph> table ...
|
||||
|
||||
@h Setting the body text.
|
||||
|
||||
@d EDOC_ALL_EXAMPLES_CLOSED -1 /* do not change this without also changing Extensions */
|
||||
|
@ -406,7 +412,7 @@ need to achieve with an HTML |<table>|.
|
|||
HTML_OPEN("blockquote");
|
||||
HTML::begin_colour(OUT, I"000080");
|
||||
mid_displayed_source_text = TRUE;
|
||||
if ((<structural-sentence>(Wordings::from(W, i))) && (ssnt == TABLE_NT))
|
||||
if (<table-sentence>(Wordings::from(W, i)))
|
||||
start_table_next_line = TRUE;
|
||||
}
|
||||
indentation--;
|
|
@ -150,14 +150,20 @@ int outcome_image_style = SIDE_OUTCOME_IMAGE_STYLE;
|
|||
|
||||
void HTMLFiles::html_outcome_image(OUTPUT_STREAM, char *image, char *verdict) {
|
||||
char *vn = "";
|
||||
if (internal_error_thrown == FALSE) {
|
||||
int be_festive = TRUE;
|
||||
#ifdef CORE_MODULE
|
||||
if (internal_error_thrown == FALSE) be_festive = FALSE;
|
||||
#endif
|
||||
if (be_festive) {
|
||||
switch (Time::feast()) {
|
||||
case CHRISTMAS_FEAST: vn = "_2"; break;
|
||||
case EASTER_FEAST: vn = "_3"; break;
|
||||
}
|
||||
if (vn[0]) outcome_image_style = CENTRED_OUTCOME_IMAGE_STYLE;
|
||||
}
|
||||
#ifdef PROBLEMS_MODULE
|
||||
Problems::Issue::issue_problems_banner(OUT, verdict);
|
||||
#endif
|
||||
switch (outcome_image_style) {
|
||||
case CENTRED_OUTCOME_IMAGE_STYLE:
|
||||
HTML_OPEN("p");
|
||||
|
@ -197,11 +203,16 @@ void HTMLFiles::outcome_image_tail(OUTPUT_STREAM) {
|
|||
void HTMLFiles::html_header(OUTPUT_STREAM, text_stream *title) {
|
||||
HTML::declare_as_HTML(OUT, FALSE);
|
||||
HTML::begin_head(OUT, NULL);
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"main.js"));
|
||||
pathname *models = Extensions::Census::doc_models();
|
||||
if (models) {
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(models, I"main.css"));
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(models, I"main.js"));
|
||||
}
|
||||
#ifdef INDEX_MODULE
|
||||
Index::scripting(OUT);
|
||||
#endif
|
||||
HTML::end_head(OUT);
|
||||
HTML::begin_body(OUT, NULL);
|
||||
HTML::comment(OUT, I"CONTENT BEGINS");
|
||||
|
@ -250,7 +261,12 @@ int source_ref_field = -1; /* which field we are buffering */
|
|||
void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
|
||||
if (source_ref_field >= 0) {
|
||||
if (source_ref_fields[source_ref_field] == NULL) source_ref_fields[source_ref_field] = Str::new();
|
||||
#ifdef PROBLEMS_MODULE
|
||||
if (charcode != SOURCE_REF_CHAR) { PUT_TO(source_ref_fields[source_ref_field], charcode); return; }
|
||||
#endif
|
||||
#ifndef PROBLEMS_MODULE
|
||||
PUT_TO(source_ref_fields[source_ref_field], charcode); return;
|
||||
#endif
|
||||
}
|
||||
switch(charcode) {
|
||||
case '"': WRITE("""); return;
|
||||
|
@ -258,6 +274,7 @@ void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
|
|||
case '>': WRITE(">"); return;
|
||||
case '&': WRITE("&"); break;
|
||||
case NEWLINE_IN_STRING: HTML_TAG("br"); return;
|
||||
#ifdef PROBLEMS_MODULE
|
||||
case FORCE_NEW_PARA_CHAR: HTML_CLOSE("p"); HTML_OPEN_WITH("p", "class=\"in2\"");
|
||||
HTMLFiles::html_icon_with_tooltip(OUT, "ornament_flower.png", NULL, NULL);
|
||||
WRITE(" "); return;
|
||||
|
@ -271,6 +288,7 @@ void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
|
|||
HTMLFiles::html_source_link(OUT, sl, TRUE);
|
||||
} else Str::clear(source_ref_fields[source_ref_field]);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
PUT(charcode);
|
||||
return;
|
||||
|
@ -291,189 +309,3 @@ void HTMLFiles::write_xml_safe_text(OUTPUT_STREAM, text_stream *txt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@h Bibliographic text.
|
||||
"Bibliographic text" is text used in bibliographic data about the work
|
||||
of IF compiled: for instance, in the iFiction record, or in the Library
|
||||
Card section of the HTML index. Note that the exact output format depends
|
||||
on global variables, which allow the bibliographic text writing code to
|
||||
configure NI for its current purposes. On non-empty strings this routine
|
||||
therefore splits into one of three independent methods.
|
||||
|
||||
=
|
||||
void HTMLFiles::compile_bibliographic_text(OUTPUT_STREAM, wchar_t *p) {
|
||||
if (p == NULL) return;
|
||||
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_XML_CMODE))
|
||||
@<Compile bibliographic text as XML respecting Treaty of Babel rules@>;
|
||||
if (TEST_COMPILATION_MODE(TRUNCATE_TEXT_CMODE))
|
||||
@<Compile bibliographic text as a truncated filename@>;
|
||||
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_I6_CMODE))
|
||||
@<Compile bibliographic text as an I6 string@>
|
||||
@<Compile bibliographic text as HTML@>;
|
||||
}
|
||||
|
||||
@ This looks like a standard routine for converting ISO Latin-1 to UTF-8
|
||||
with XML escapes, but there are a few conventions on whitespace, too, in order
|
||||
to comply with a strict reading of the Treaty of Babel. (This is intended
|
||||
for fields in iFiction records.)
|
||||
|
||||
@<Compile bibliographic text as XML respecting Treaty of Babel rules@> =
|
||||
int i = 0, i2 = Wide::len(p)-1, snl, wsc;
|
||||
if ((p[0] == '"') && (p[i2] == '"')) { i++; i2--; } /* omit surrounding double-quotes */
|
||||
while (Characters::is_babel_whitespace(p[i])) i++; /* omit leading whitespace */
|
||||
while ((i2>=0) && (Characters::is_babel_whitespace(p[i2]))) i2--; /* omit trailing whitespace */
|
||||
for (snl = FALSE, wsc = 0; i<=i2; i++) {
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t':
|
||||
snl = FALSE;
|
||||
wsc++;
|
||||
int k = i;
|
||||
while ((p[k] == ' ') || (p[k] == '\x0a') || (p[k] == '\x0d') || (p[k] == '\t')) k++;
|
||||
if ((wsc == 1) && (p[k] != NEWLINE_IN_STRING)) WRITE(" ");
|
||||
break;
|
||||
case NEWLINE_IN_STRING:
|
||||
if (snl) break;
|
||||
WRITE("<br/>");
|
||||
snl = TRUE; wsc = 1; break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
WRITE("'"); break;
|
||||
}
|
||||
int n = CompiledText::expand_unisub(OUT, p, i);
|
||||
if (n >= 0) { i = n; break; }
|
||||
/* and otherwise fall through to the default case */
|
||||
default:
|
||||
snl = FALSE;
|
||||
wsc = 0;
|
||||
switch(p[i]) {
|
||||
case '&': WRITE("&"); break;
|
||||
case '<': WRITE("<"); break;
|
||||
case '>': WRITE(">"); break;
|
||||
default: PUT(p[i]); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ In the HTML version, we want to respect the forcing of newlines, and
|
||||
also the |[']| escape to obtain a literal single quotation mark.
|
||||
|
||||
@<Compile bibliographic text as HTML@> =
|
||||
int i, whitespace_count=0;
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t':
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case NEWLINE_IN_STRING:
|
||||
while (p[i+1] == NEWLINE_IN_STRING) i++;
|
||||
PUT('<');
|
||||
PUT('p');
|
||||
PUT('>');
|
||||
whitespace_count = 1;
|
||||
break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
PUT('\''); break;
|
||||
}
|
||||
int n = CompiledText::expand_unisub(OUT, p, i);
|
||||
if (n >= 0) { i = n; break; }
|
||||
/* and otherwise fall through to the default case */
|
||||
default:
|
||||
whitespace_count = 0;
|
||||
PUT(p[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ In the Inform 6 string version, we suppress the forcing of newlines, but
|
||||
otherwise it's much the same.
|
||||
|
||||
@<Compile bibliographic text as an I6 string@> =
|
||||
int i, whitespace_count=0;
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
PUT('\''); break;
|
||||
} /* and otherwise fall through to the default case */
|
||||
default:
|
||||
whitespace_count = 0;
|
||||
PUT(p[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ This code is used to work out a good filename for something given a name
|
||||
inside NI. For instance, if a project is called
|
||||
|
||||
>> "St. Bartholemew's Fair: \'Etude for a Push-Me/Pull-You Machine"
|
||||
|
||||
then what would be a good filename for its released story file?
|
||||
|
||||
In the filename version we must forcibly truncate the text to ensure
|
||||
that it does not exceed a certain length, and must also make it filename-safe,
|
||||
omitting characters used as folder separators on various platforms and
|
||||
(for good measure) removing accents from accented letters, so that we can
|
||||
arrive at a sequence of ASCII characters. Each run of whitespace is also
|
||||
converted to a single space. If this would result in an empty text or only
|
||||
a single space, we return the text "story" instead.
|
||||
|
||||
Our example (if not truncated) then emerges as:
|
||||
|
||||
|St- Bartholemew's Fair- Etude for a Push-Me-Pull-You Machine|
|
||||
|
||||
Note that we do not write any filename extension (e.g., |.z5|) here.
|
||||
|
||||
We change possible filename separators or extension indicators to hyphens,
|
||||
and remove accents from each possible ISO Latin-1 accented letter. This does
|
||||
still mean that the OE and AE digraphs will simply be omitted, while the
|
||||
German eszet will be barbarously shortened to a single "s", but life is
|
||||
just too short to care overmuch about this.
|
||||
|
||||
@<Compile bibliographic text as a truncated filename@> =
|
||||
int i, pos = STREAM_EXTENT(OUT), whitespace_count=0, black_chars_written = 0;
|
||||
int N = 100;
|
||||
#ifdef IF_MODULE
|
||||
N = BIBLIOGRAPHIC_TEXT_TRUNCATION;
|
||||
#endif
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if (STREAM_EXTENT(OUT) - pos >= N) break;
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case '?': case '*':
|
||||
if ((p[i+1]) && (p[i+1] != '\"')) PUT('-');
|
||||
break;
|
||||
default: {
|
||||
int charcode = p[i];
|
||||
charcode = Characters::make_filename_safe(charcode);
|
||||
whitespace_count = 0;
|
||||
if (charcode < 128) {
|
||||
PUT(charcode); black_chars_written++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (black_chars_written == 0) WRITE("story");
|
||||
return;
|
14
inbuild/html-module/Contents.w
Normal file
14
inbuild/html-module/Contents.w
Normal file
|
@ -0,0 +1,14 @@
|
|||
Title: html
|
||||
Author: Graham Nelson
|
||||
Purpose: HTML and Javascript generation tools.
|
||||
Language: InC
|
||||
Licence: Artistic License 2.0
|
||||
|
||||
Chapter 1: Starting Up
|
||||
HTML Module
|
||||
|
||||
Chapter 2: HTML
|
||||
"HTML and JavaScript generation."
|
||||
HTML Files
|
||||
Javascript Pastes
|
||||
HTML Documentation
|
|
@ -46,6 +46,7 @@ to add and process command line switches handled by inbuild:
|
|||
@e DEBUG_CLSW
|
||||
@e FORMAT_CLSW
|
||||
@e RELEASE_CLSW
|
||||
@e CENSUS_CLSW
|
||||
|
||||
=
|
||||
void Inbuild::declare_options(void) {
|
||||
|
@ -71,6 +72,8 @@ void Inbuild::declare_options(void) {
|
|||
L"compile I6 code suitable for the virtual machine X");
|
||||
CommandLine::declare_switch(SOURCE_CLSW, L"source", 2,
|
||||
L"use file X as the Inform source text");
|
||||
CommandLine::declare_boolean_switch(CENSUS_CLSW, L"census", 1,
|
||||
L"perform an extensions census");
|
||||
CommandLine::end_group();
|
||||
}
|
||||
|
||||
|
@ -78,6 +81,7 @@ pathname *shared_transient_resources = NULL;
|
|||
int this_is_a_debug_compile = FALSE; /* Destined to be compiled with debug features */
|
||||
int this_is_a_release_compile = FALSE; /* Omit sections of source text marked not for release */
|
||||
text_stream *story_filename_extension = NULL; /* What story file we will eventually have */
|
||||
int census_mode = FALSE; /* Running only to update extension documentation */
|
||||
|
||||
void Inbuild::option(int id, int val, text_stream *arg, void *state) {
|
||||
RUN_ONLY_IN_PHASE(CONFIGURATION_INBUILD_PHASE)
|
||||
|
@ -98,6 +102,7 @@ void Inbuild::option(int id, int val, text_stream *arg, void *state) {
|
|||
if (Inbuild::set_I7_source(arg) == FALSE)
|
||||
Errors::fatal_with_text("can't specify the source file twice: '%S'", arg);
|
||||
break;
|
||||
case CENSUS_CLSW: census_mode = val; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +170,7 @@ void Inbuild::go_operational(void) {
|
|||
LOOP_OVER(C, inbuild_copy)
|
||||
Copies::go_operational(C);
|
||||
inbuild_phase = OPERATIONAL_INBUILD_PHASE;
|
||||
if (census_mode) Extensions::Census::handle_census_mode();
|
||||
}
|
||||
|
||||
@h The nest list.
|
||||
|
|
|
@ -31,6 +31,8 @@ Setting up the use of this module.
|
|||
@e inform_language_MT
|
||||
@e inform_pipeline_MT
|
||||
@e copy_error_MT
|
||||
@e extension_dictionary_entry_MT
|
||||
@e known_extension_clash_MT
|
||||
|
||||
=
|
||||
ALLOCATE_INDIVIDUALLY(inform_kit)
|
||||
|
@ -55,6 +57,8 @@ ALLOCATE_INDIVIDUALLY(inform_project)
|
|||
ALLOCATE_INDIVIDUALLY(inform_language)
|
||||
ALLOCATE_INDIVIDUALLY(inform_pipeline)
|
||||
ALLOCATE_INDIVIDUALLY(copy_error)
|
||||
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
|
||||
ALLOCATE_INDIVIDUALLY(known_extension_clash)
|
||||
|
||||
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)
|
||||
|
||||
|
|
|
@ -728,3 +728,148 @@ void Extensions::Census::write_icons(OUTPUT_STREAM, compatibility_specification
|
|||
if (Compatibility::with(C, VM))
|
||||
Extensions::Census::plot_icon(OUT, VM);
|
||||
}
|
||||
|
||||
@h Updating the documentation.
|
||||
This is done in the course of taking an extension census, which is called
|
||||
for in one of two circumstances: when Inform is being run in "census mode" to
|
||||
notify it that extensions have been installed or uninstalled; or when Inform
|
||||
has completed the successful compilation of a source text. In the latter
|
||||
case, it knows quite a lot about the extensions actually used in that
|
||||
compilation, and so can write detailed versions of their documentation:
|
||||
since it is updating extension documentation anyway, it conducts a census
|
||||
as well. (In both cases the extension dictionary is also worked upon.) The
|
||||
two alternatives are expressed here:
|
||||
|
||||
=
|
||||
void Extensions::Census::handle_census_mode(void) {
|
||||
extension_census *C = Extensions::Census::new();
|
||||
Extensions::Dictionary::load();
|
||||
Extensions::Census::perform(C);
|
||||
Extensions::Census::write_top_level_of_extensions_documentation(C);
|
||||
Extensions::Census::write_sketchy_documentation_for_extensions_found(TRUE);
|
||||
}
|
||||
|
||||
void Extensions::Census::update_census(void) {
|
||||
Extensions::Dictionary::load();
|
||||
extension_census *C = Extensions::Census::new();
|
||||
Extensions::Census::perform(C);
|
||||
Extensions::Census::write_top_level_of_extensions_documentation(C);
|
||||
#ifdef CORE_MODULE
|
||||
inform_extension *E;
|
||||
LOOP_OVER(E, inform_extension) Extensions::Documentation::write_detailed(E);
|
||||
#endif
|
||||
Extensions::Census::write_sketchy_documentation_for_extensions_found(FALSE);
|
||||
Extensions::Dictionary::write_back();
|
||||
if (Log::aspect_switched_on(EXTENSIONS_CENSUS_DA)) Works::log_work_hash_table();
|
||||
}
|
||||
|
||||
@ Documenting extensions seen but not used: we run through the census
|
||||
results in no particular order and create a sketchy page of documentation,
|
||||
if there's no better one already.
|
||||
|
||||
=
|
||||
void Extensions::Census::write_sketchy_documentation_for_extensions_found(int census_mode) {
|
||||
extension_census_datum *ecd;
|
||||
LOOP_OVER(ecd, extension_census_datum)
|
||||
Extensions::Documentation::write_sketchy(ecd, census_mode);
|
||||
}
|
||||
|
||||
@h Writing the extensions home pages.
|
||||
Extensions documentation forms a mini-website within the Inform
|
||||
documentation. There is a top level consisting of two home pages: a
|
||||
directory of all installed extensions, and an index to the terms defined in
|
||||
those extensions. A cross-link switches between them. Each of these links
|
||||
down to the bottom level, where there is a page for every installed
|
||||
extension (wherever it is installed). The picture is therefore something
|
||||
like this:
|
||||
|
||||
= (not code)
|
||||
(Main documentation contents page)
|
||||
|
|
||||
Extensions.html--ExtIndex.html
|
||||
| \/ |
|
||||
| /\ |
|
||||
Nigel Toad/Eggs Barnabas Dundritch/Neopolitan Iced Cream ...
|
||||
|
||||
@ These pages are stored at the relative pathnames
|
||||
|
||||
|Extensions/Documentation/Extensions.html|
|
||||
|Extensions/Documentation/ExtIndex.html|
|
||||
|
||||
They are made by inserting content in place of the material between the
|
||||
HTML anchors |on| and |off| in a template version of the page built in
|
||||
to the application, with a leafname which varies from platform to
|
||||
platform, for reasons as always to do with the vagaries of Internet
|
||||
Explorer 7 for Windows.
|
||||
|
||||
=
|
||||
void Extensions::Census::write_top_level_of_extensions_documentation(extension_census *C) {
|
||||
Extensions::Census::write_top_level_extensions_page(I"Extensions.html", 1, C);
|
||||
Extensions::Census::write_top_level_extensions_page(I"ExtIndex.html", 2, NULL);
|
||||
}
|
||||
|
||||
@ =
|
||||
pathname *Extensions::Census::doc_pathname(void) {
|
||||
pathname *P = Inbuild::transient();
|
||||
if (P == NULL) return NULL;
|
||||
if (Pathnames::create_in_file_system(P) == 0) return NULL;
|
||||
P = Pathnames::subfolder(P, I"Documentation");
|
||||
if (Pathnames::create_in_file_system(P) == 0) return NULL;
|
||||
return P;
|
||||
}
|
||||
pathname *Extensions::Census::doc_models(void) {
|
||||
inbuild_nest *I = Inbuild::internal(); if (I == NULL) return NULL;
|
||||
return Pathnames::subfolder(I->location, I"HTML");
|
||||
}
|
||||
void Extensions::Census::write_top_level_extensions_page(text_stream *leaf, int content, extension_census *C) {
|
||||
pathname *P = Extensions::Census::doc_pathname();
|
||||
if (P == NULL) return;
|
||||
filename *F = Filenames::in_folder(P, leaf);
|
||||
|
||||
pathname *models = Extensions::Census::doc_models();
|
||||
if (models == NULL) return;
|
||||
|
||||
text_stream HOMEPAGE_struct;
|
||||
text_stream *OUT = &HOMEPAGE_struct;
|
||||
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE) {
|
||||
#ifdef CORE_MODULE
|
||||
Problems::Fatal::filename_related(
|
||||
"Unable to open extensions documentation index for writing", F);
|
||||
#endif
|
||||
#ifndef CORE_MODULE
|
||||
Errors::fatal_with_file("extensions documentation index for writing: %f", F);
|
||||
#endif
|
||||
}
|
||||
|
||||
HTML::declare_as_HTML(OUT, FALSE);
|
||||
|
||||
HTML::begin_head(OUT, NULL);
|
||||
HTML::title(OUT, I"Extensions");
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(models, I"extensions.js"));
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(models, I"main.css"));
|
||||
HTML::end_head(OUT);
|
||||
|
||||
HTML::begin_body(OUT, NULL);
|
||||
HTML::begin_html_table(OUT, NULL, TRUE, 0, 4, 0, 0, 0);
|
||||
HTML::first_html_column(OUT, 0);
|
||||
HTML_TAG_WITH("img", "src='inform:/doc_images/extensions@2x.png' border=0 width=150 height=150");
|
||||
HTML::next_html_column(OUT, 0);
|
||||
|
||||
HTML_OPEN_WITH("div", "class=\"headingboxDark\"");
|
||||
HTML_OPEN_WITH("div", "class=\"headingtextWhite\"");
|
||||
WRITE("Installed Extensions");
|
||||
HTML_CLOSE("div");
|
||||
HTML_OPEN_WITH("div", "class=\"headingrubricWhite\"");
|
||||
WRITE("Bundles of extra rules or phrases to extend what Inform can do");
|
||||
HTML_CLOSE("div");
|
||||
HTML_CLOSE("div");
|
||||
|
||||
switch (content) {
|
||||
case 1: Extensions::Census::write_results(OUT, C); break;
|
||||
case 2: Extensions::Dictionary::write_to_HTML(OUT); break;
|
||||
}
|
||||
|
||||
HTML::end_body(OUT);
|
||||
}
|
||||
|
|
|
@ -215,11 +215,20 @@ Not a surprising routine: open, convert one line at a time to dictionary
|
|||
entries, close.
|
||||
|
||||
=
|
||||
filename *Extensions::Dictionary::filename(void) {
|
||||
pathname *P = Inbuild::transient();
|
||||
if (P == NULL) return NULL;
|
||||
return Filenames::in_folder(P, I"Dictionary.txt");
|
||||
}
|
||||
|
||||
void Extensions::Dictionary::load(void) {
|
||||
filename *F = Extensions::Dictionary::filename();
|
||||
if (F == NULL) return;
|
||||
|
||||
@<Ensure the serialised extensions dictionary file exists@>;
|
||||
|
||||
LOGIF(EXTENSIONS_CENSUS, "Reading dictionary file\n");
|
||||
TextFiles::read(filename_of_extensions_dictionary, FALSE,
|
||||
TextFiles::read(F, FALSE,
|
||||
NULL, FALSE, Extensions::Dictionary::load_helper, NULL, NULL);
|
||||
LOGIF(EXTENSIONS_CENSUS, "Finished reading dictionary file\n");
|
||||
}
|
||||
|
@ -233,10 +242,10 @@ file, we are then unable to open it again: that must mean a file I/O error of
|
|||
some kind, which is bad enough news to bother the user with.
|
||||
|
||||
@<Ensure the serialised extensions dictionary file exists@> =
|
||||
FILE *DICTF = Filenames::fopen(filename_of_extensions_dictionary, "r");
|
||||
FILE *DICTF = Filenames::fopen(F, "r");
|
||||
if (DICTF == NULL) {
|
||||
LOGIF(EXTENSIONS_CENSUS, "Creating new empty dictionary file\n");
|
||||
FILE *EMPTY_DICTF = Filenames::fopen(filename_of_extensions_dictionary, "w");
|
||||
FILE *EMPTY_DICTF = Filenames::fopen(F, "w");
|
||||
if (EMPTY_DICTF == NULL) return;
|
||||
fclose(EMPTY_DICTF);
|
||||
}
|
||||
|
@ -305,7 +314,9 @@ void Extensions::Dictionary::write_back(void) {
|
|||
text_stream DICTF_struct;
|
||||
text_stream *DICTF = &DICTF_struct;
|
||||
|
||||
if (STREAM_OPEN_TO_FILE(DICTF, filename_of_extensions_dictionary, UTF8_ENC) == FALSE) return;
|
||||
filename *F = Extensions::Dictionary::filename();
|
||||
if (F == NULL) return;
|
||||
if (STREAM_OPEN_TO_FILE(DICTF, F, UTF8_ENC) == FALSE) return;
|
||||
|
||||
LOGIF(EXTENSIONS_CENSUS, "Writing dictionary file\n");
|
||||
|
|
@ -10,10 +10,10 @@ understand what it entails). The following routine writes both kinds of page.
|
|||
|
||||
=
|
||||
void Extensions::Documentation::write_detailed(inform_extension *E) {
|
||||
Extensions::Documentation::write_extension_documentation(NULL, E);
|
||||
Extensions::Documentation::write_extension_documentation(NULL, E, FALSE);
|
||||
}
|
||||
void Extensions::Documentation::write_sketchy(extension_census_datum *ecd) {
|
||||
Extensions::Documentation::write_extension_documentation(ecd, NULL);
|
||||
void Extensions::Documentation::write_sketchy(extension_census_datum *ecd, int census_mode) {
|
||||
Extensions::Documentation::write_extension_documentation(ecd, NULL, census_mode);
|
||||
}
|
||||
|
||||
@ Thus we pass two arguments, |ecd| and |ef|, to |Extensions::Documentation::write_extension_documentation|:
|
||||
|
@ -35,11 +35,11 @@ in sequence,
|
|||
where these are pathnames relative to the external resources area.
|
||||
|
||||
=
|
||||
void Extensions::Documentation::write_extension_documentation(extension_census_datum *ecd, inform_extension *E) {
|
||||
void Extensions::Documentation::write_extension_documentation(extension_census_datum *ecd, inform_extension *E, int census_mode) {
|
||||
int c, eg_count;
|
||||
eg_count = Extensions::Documentation::write_extension_documentation_page(ecd, E, -1);
|
||||
eg_count = Extensions::Documentation::write_extension_documentation_page(ecd, E, -1, census_mode);
|
||||
for (c=1; c<=eg_count; c++)
|
||||
Extensions::Documentation::write_extension_documentation_page(ecd, E, c);
|
||||
Extensions::Documentation::write_extension_documentation_page(ecd, E, c, census_mode);
|
||||
}
|
||||
|
||||
@ Here then is the nub of it. An ECD is not really enough information to go on.
|
||||
|
@ -57,7 +57,7 @@ is any, as well as the correct identifying headings and requirements.
|
|||
|
||||
=
|
||||
int Extensions::Documentation::write_extension_documentation_page(extension_census_datum *ecd, inform_extension *E,
|
||||
int eg_number) {
|
||||
int eg_number, int census_mode) {
|
||||
inbuild_work *work = NULL;
|
||||
text_stream DOCF_struct;
|
||||
text_stream *DOCF = &DOCF_struct;
|
||||
|
@ -71,7 +71,7 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
|
|||
TEMPORARY_TEXT(leaf);
|
||||
Str::copy(leaf, work->title);
|
||||
if (eg_number > 0) WRITE_TO(leaf, "-eg%d", eg_number);
|
||||
filename *name = Locations::of_extension_documentation(leaf, work->author_name);
|
||||
filename *name = Extensions::Documentation::location(leaf, work->author_name);
|
||||
|
||||
page_exists_already = FALSE;
|
||||
TEST_DOCF = Filenames::fopen(name, "r");
|
||||
|
@ -86,8 +86,9 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
|
|||
}
|
||||
if (E == NULL) internal_error("null E in extension documentation writer");
|
||||
|
||||
if (Pathnames::create_in_file_system(
|
||||
Pathnames::subfolder(pathname_of_extension_docs_inner, work->author_name)) == 0)
|
||||
pathname *P = Extensions::Documentation::path();
|
||||
if (P == NULL) return 0;
|
||||
if (Pathnames::create_in_file_system(Pathnames::subfolder(P, work->author_name)) == 0)
|
||||
return 0;
|
||||
|
||||
if (STREAM_OPEN_TO_FILE(DOCF, name, UTF8_ENC) == FALSE)
|
||||
|
@ -99,6 +100,27 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
|
|||
return no_egs;
|
||||
}
|
||||
|
||||
pathname *Extensions::Documentation::path(void) {
|
||||
pathname *P = Inbuild::transient();
|
||||
if ((P == NULL) || (Pathnames::create_in_file_system(P) == 0)) return NULL;
|
||||
P = Pathnames::subfolder(P, I"Extensions");
|
||||
if (Pathnames::create_in_file_system(P) == 0) return NULL;
|
||||
P = Pathnames::subfolder(P, I"Documentation");
|
||||
if (Pathnames::create_in_file_system(P) == 0) return NULL;
|
||||
return P;
|
||||
}
|
||||
|
||||
filename *Extensions::Documentation::location(text_stream *title, text_stream *author) {
|
||||
pathname *P = Extensions::Documentation::path();
|
||||
if (P == NULL) return NULL;
|
||||
|
||||
TEMPORARY_TEXT(leaf);
|
||||
WRITE_TO(leaf, "%S.html", title);
|
||||
filename *F = Filenames::in_folder(Pathnames::subfolder(P, author), leaf);
|
||||
DISCARD_TEXT(leaf);
|
||||
return F;
|
||||
}
|
||||
|
||||
@ The reader may wonder why we perform the conversion in this slightly recursive
|
||||
way, by calling our parent routine again. Wouldn't it be simpler just to set
|
||||
|ecd| to null and let events take their course? The answer is that this would
|
||||
|
@ -110,45 +132,36 @@ our EF, and return 0 in response to the ECD call to prevent any further ECD
|
|||
calls.
|
||||
|
||||
@<Convert ECD to a text-only EF@> =
|
||||
feed_t id = Feeds::begin();
|
||||
Feeds::feed_stream(work->raw_author_name);
|
||||
Feeds::feed_text(L" ");
|
||||
wording AW = Feeds::end(id);
|
||||
|
||||
id = Feeds::begin();
|
||||
Feeds::feed_stream(work->raw_title);
|
||||
Feeds::feed_text(L" ");
|
||||
wording TW = Feeds::end(id);
|
||||
|
||||
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
|
||||
if (<unsuitable-name>(AW)) return 0;
|
||||
if (<unsuitable-name>(TW)) return 0;
|
||||
inbuild_requirement *req = Requirements::any_version_of(work);
|
||||
E = Extensions::Inclusion::load(req);
|
||||
E = Extensions::Documentation::load(work);
|
||||
if (E == NULL) return 0; /* shouldn't happen: it was there only moments ago */
|
||||
Extensions::Documentation::write_extension_documentation(NULL, E);
|
||||
Copies::read_source_text_for(E->as_copy);
|
||||
Extensions::Documentation::write_extension_documentation(NULL, E, census_mode);
|
||||
|
||||
@ We now make much the same "paste into the gap in the template" copying
|
||||
exercise as when generating the home pages for extensions, though with a
|
||||
different template:
|
||||
|
||||
@<Write the actual extension documentation page to DOCF@> =
|
||||
text_stream *OUT = DOCF;
|
||||
HTML::declare_as_HTML(OUT, FALSE);
|
||||
pathname *models = Extensions::Census::doc_models();
|
||||
if (models) {
|
||||
text_stream *OUT = DOCF;
|
||||
HTML::declare_as_HTML(OUT, FALSE);
|
||||
|
||||
HTML::begin_head(OUT, NULL);
|
||||
HTML::title(OUT, I"Extension");
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"extensionfile.js"));
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
|
||||
HTML::end_head(OUT);
|
||||
HTML::begin_head(OUT, NULL);
|
||||
HTML::title(OUT, I"Extension");
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(models, I"extensionfile.js"));
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(models, I"main.css"));
|
||||
HTML::end_head(OUT);
|
||||
|
||||
HTML::begin_body(OUT, NULL);
|
||||
HTML::incorporate_HTML(OUT,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"extensionfile.html"));
|
||||
@<Write documentation for a specific extension into the page@>;
|
||||
HTML::end_body(OUT);
|
||||
HTML::begin_body(OUT, NULL);
|
||||
HTML::incorporate_HTML(OUT,
|
||||
Filenames::in_folder(models, I"extensionfile.html"));
|
||||
@<Write documentation for a specific extension into the page@>;
|
||||
HTML::end_body(OUT);
|
||||
}
|
||||
|
||||
@ And this is the body:
|
||||
|
||||
|
@ -168,7 +181,9 @@ different template:
|
|||
HTML_CLOSE("p");
|
||||
@<Write up the rubric, if any@>;
|
||||
@<Write up the table of contents for the extension author's supplied documentation, if any@>;
|
||||
@<Document and dictionary the definitions made in extension file E@>;
|
||||
#ifdef CORE_MODULE
|
||||
Extensions::Files::document_in_detail(OUT, E);
|
||||
#endif
|
||||
HTML_TAG("hr");
|
||||
@<Write up the extension author's supplied documentation, if any@>;
|
||||
|
||||
|
@ -233,175 +248,34 @@ easily be scrolled down off screen when the user first visits the page.
|
|||
}
|
||||
}
|
||||
|
||||
@ Nothing can prevent a certain repetitiousness intruding here, but there is
|
||||
just enough local knowledge required to make it foolhardy to try to automate
|
||||
this from a dump of the excerpt meanings table (say). The ordering of
|
||||
paragraphs, as in Roget's Thesaurus, tries to proceed from solid things
|
||||
through to diffuse linguistic ones. But the reader of the resulting
|
||||
documentation page could be forgiven for thinking it a miscellany.
|
||||
|
||||
@<Document and dictionary the definitions made in extension file E@> =
|
||||
Extensions::Dictionary::erase_entries(E);
|
||||
if (E) Extensions::Dictionary::time_stamp(E);
|
||||
|
||||
@<Document and dictionary the kinds made in extension@>;
|
||||
@<Document and dictionary the objects made in extension@>;
|
||||
|
||||
@<Document and dictionary the global variables made in extension@>;
|
||||
@<Document and dictionary the enumerated constant values made in extension@>;
|
||||
|
||||
@<Document and dictionary the kinds of action made in extension@>;
|
||||
@<Document and dictionary the actions made in extension@>;
|
||||
|
||||
@<Document and dictionary the verbs made in extension@>;
|
||||
@<Document and dictionary the adjectival phrases made in extension@>;
|
||||
@<Document and dictionary the property names made in extension@>;
|
||||
|
||||
@<Document and dictionary the use options made in extension@>;
|
||||
|
||||
@ Off we go, then. Kinds of object:
|
||||
|
||||
@<Document and dictionary the kinds made in extension@> =
|
||||
kind *K;
|
||||
int kc = 0;
|
||||
LOOP_OVER_BASE_KINDS(K) {
|
||||
parse_node *S = Kinds::Behaviour::get_creating_sentence(K);
|
||||
if (S) {
|
||||
if (Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(S))) == E->read_into_file) {
|
||||
wording W = Kinds::Behaviour::get_name(K, FALSE);
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Kinds", I"kind", W);
|
||||
kind *S = Kinds::Compare::super(K);
|
||||
if (S) {
|
||||
W = Kinds::Behaviour::get_name(S, FALSE);
|
||||
if (Wordings::nonempty(W)) WRITE(" (a kind of %+W)", W);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Actual objects:
|
||||
|
||||
@<Document and dictionary the objects made in extension@> =
|
||||
instance *I;
|
||||
int kc = 0;
|
||||
LOOP_OVER_OBJECT_INSTANCES(I) {
|
||||
wording OW = Instances::get_name(I, FALSE);
|
||||
if ((Instances::get_creating_sentence(I)) && (Wordings::nonempty(OW))) {
|
||||
if (Lexer::file_of_origin(
|
||||
Wordings::first_wn(ParseTree::get_text(Instances::get_creating_sentence(I))))
|
||||
== E->read_into_file) {
|
||||
TEMPORARY_TEXT(name_of_its_kind);
|
||||
kind *k = Instances::to_kind(I);
|
||||
wording W = Kinds::Behaviour::get_name(k, FALSE);
|
||||
WRITE_TO(name_of_its_kind, "%+W", W);
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E,
|
||||
"Physical creations", name_of_its_kind, OW);
|
||||
WRITE(" (a %S)", name_of_its_kind);
|
||||
DISCARD_TEXT(name_of_its_kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Global variables:
|
||||
|
||||
@<Document and dictionary the global variables made in extension@> =
|
||||
nonlocal_variable *q;
|
||||
int kc = 0;
|
||||
LOOP_OVER(q, nonlocal_variable)
|
||||
if ((Wordings::first_wn(q->name) >= 0) &&
|
||||
(NonlocalVariables::is_global(q)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(q->name)) == E->read_into_file) &&
|
||||
(Sentences::Headings::indexed(Sentences::Headings::of_wording(q->name)))) {
|
||||
if (<value-understood-variable-name>(q->name) == FALSE)
|
||||
kc = Extensions::Documentation::document_headword(OUT,
|
||||
kc, E, "Values that vary", I"value", q->name);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Constants:
|
||||
|
||||
@<Document and dictionary the enumerated constant values made in extension@> =
|
||||
instance *q;
|
||||
int kc = 0;
|
||||
LOOP_OVER_ENUMERATION_INSTANCES(q) {
|
||||
wording NW = Instances::get_name(q, FALSE);
|
||||
if ((Wordings::nonempty(NW)) && (Lexer::file_of_origin(Wordings::first_wn(NW)) == E->read_into_file))
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Values", I"value", NW);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Kinds of action:
|
||||
|
||||
@<Document and dictionary the kinds of action made in extension@> =
|
||||
#ifdef IF_MODULE
|
||||
PL::Actions::Patterns::Named::index_for_extension(OUT, E->read_into_file, E);
|
||||
#endif
|
||||
|
||||
@ Actions:
|
||||
|
||||
@<Document and dictionary the actions made in extension@> =
|
||||
#ifdef IF_MODULE
|
||||
PL::Actions::Index::index_for_extension(OUT, E->read_into_file, E);
|
||||
#endif
|
||||
|
||||
@ Verbs (this one we delegate):
|
||||
|
||||
@<Document and dictionary the verbs made in extension@> =
|
||||
Index::Lexicon::list_verbs_in_file(OUT, E->read_into_file, E);
|
||||
|
||||
@ Adjectival phrases:
|
||||
|
||||
@<Document and dictionary the adjectival phrases made in extension@> =
|
||||
adjectival_phrase *adj;
|
||||
int kc = 0;
|
||||
LOOP_OVER(adj, adjectival_phrase) {
|
||||
wording W = Adjectives::get_text(adj, FALSE);
|
||||
if ((Wordings::nonempty(W)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(W)) == E->read_into_file))
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Adjectives", I"adjective", W);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Other adjectives:
|
||||
|
||||
@<Document and dictionary the property names made in extension@> =
|
||||
property *prn;
|
||||
int kc = 0;
|
||||
LOOP_OVER(prn, property)
|
||||
if ((Wordings::nonempty(prn->name)) &&
|
||||
(Properties::is_shown_in_index(prn)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(prn->name)) == E->read_into_file))
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Properties", I"property",
|
||||
prn->name);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Use options:
|
||||
|
||||
@<Document and dictionary the use options made in extension@> =
|
||||
use_option *uo;
|
||||
int kc = 0;
|
||||
LOOP_OVER(uo, use_option)
|
||||
if ((Wordings::first_wn(uo->name) >= 0) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(uo->name)) == E->read_into_file))
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Use options", I"use option",
|
||||
uo->name);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Finally, the utility routine which keeps count (hence |kc|) and displays
|
||||
suitable lists, while entering each entry in turn into the extension
|
||||
dictionary.
|
||||
@
|
||||
|
||||
=
|
||||
int Extensions::Documentation::document_headword(OUTPUT_STREAM, int kc, inform_extension *E, char *par_heading,
|
||||
text_stream *category, wording W) {
|
||||
if (kc++ == 0) { HTML_OPEN("p"); WRITE("%s: ", par_heading); }
|
||||
else WRITE(", ");
|
||||
WRITE("<b>%+W</b>", W);
|
||||
Extensions::Dictionary::new_entry(category, E, W);
|
||||
return kc;
|
||||
}
|
||||
inform_extension *Extensions::Documentation::load(inbuild_work *work) {
|
||||
inbuild_requirement *req = Requirements::any_version_of(work);
|
||||
req->allow_malformed = TRUE;
|
||||
|
||||
@ And that at last brings us to a milestone: the end of the Land of Extensions.
|
||||
We can return to Inform's more usual concerns.
|
||||
inform_extension *E;
|
||||
LOOP_OVER(E, inform_extension)
|
||||
if (Requirements::meets(E->as_copy->edition, req)) {
|
||||
Extensions::must_satisfy(E, req);
|
||||
return E;
|
||||
}
|
||||
|
||||
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
||||
Nests::search_for(req, Inbuild::nest_list(), L);
|
||||
inbuild_search_result *search_result;
|
||||
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
|
||||
E = ExtensionManager::from_copy(search_result->copy);
|
||||
int origin = Nests::get_tag(search_result->nest);
|
||||
switch (origin) {
|
||||
case MATERIALS_NEST_TAG:
|
||||
case EXTERNAL_NEST_TAG:
|
||||
E->loaded_from_built_in_area = FALSE; break;
|
||||
case INTERNAL_NEST_TAG:
|
||||
E->loaded_from_built_in_area = TRUE; break;
|
||||
}
|
||||
return E;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -19,8 +19,8 @@ typedef struct inform_extension {
|
|||
struct text_stream *extra_credit_as_lexed;
|
||||
struct source_file *read_into_file; /* Which source file loaded this */
|
||||
struct inbuild_requirement *must_satisfy;
|
||||
#ifdef CORE_MODULE
|
||||
int loaded_from_built_in_area; /* Located within Inform application */
|
||||
#ifdef CORE_MODULE
|
||||
struct parse_node *inclusion_sentence; /* Where the source called for this */
|
||||
#endif
|
||||
MEMORY_MANAGEMENT
|
||||
|
@ -40,8 +40,8 @@ void Extensions::scan(inbuild_genre *G, inbuild_copy *C) {
|
|||
E->rubric_as_lexed = Str::new();
|
||||
E->extra_credit_as_lexed = NULL;
|
||||
E->must_satisfy = NULL;
|
||||
#ifdef CORE_MODULE
|
||||
E->loaded_from_built_in_area = FALSE;
|
||||
#ifdef CORE_MODULE
|
||||
E->inclusion_sentence = NULL;
|
||||
#endif
|
||||
|
||||
|
@ -282,17 +282,18 @@ void Extensions::make_standard(inform_extension *E) {
|
|||
E->standard = TRUE;
|
||||
}
|
||||
|
||||
#ifdef CORE_MODULE
|
||||
void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
|
||||
if (E->must_satisfy == NULL) E->must_satisfy = req;
|
||||
else {
|
||||
semantic_version_number V = req->min_version;
|
||||
if (VersionNumbers::is_null(V) == FALSE)
|
||||
if (Requirements::ratchet_minimum(V, E->must_satisfy))
|
||||
if (Requirements::ratchet_minimum(V, E->must_satisfy)) {
|
||||
#ifdef CORE_MODULE
|
||||
Extensions::set_inclusion_sentence(E, current_sentence);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int Extensions::satisfies(inform_extension *E) {
|
||||
if (E == NULL) return FALSE;
|
||||
|
@ -305,9 +306,7 @@ int Extensions::satisfies(inform_extension *E) {
|
|||
void Extensions::read_source_text_for(inform_extension *E) {
|
||||
filename *F = E->as_copy->location_if_file;
|
||||
int doc_only = FALSE;
|
||||
#ifdef CORE_MODULE
|
||||
if (CoreMain::census_mode()) doc_only = TRUE;
|
||||
#endif
|
||||
if (census_mode) doc_only = TRUE;
|
||||
TEMPORARY_TEXT(synopsis);
|
||||
@<Concoct a synopsis for the extension to be read@>;
|
||||
E->read_into_file = SourceText::read_file(E->as_copy, F, synopsis, doc_only, FALSE);
|
||||
|
|
|
@ -36,6 +36,8 @@ Chapter 5: Services for the Inform Compiler
|
|||
Virtual Machine Grammar
|
||||
Kit Services
|
||||
Extension Services
|
||||
Extension Dictionary
|
||||
Extension Documentation
|
||||
Extension Census
|
||||
Template Services
|
||||
Project Services
|
||||
|
|
|
@ -54,6 +54,7 @@ int Main::core_inform_main(int argc, char *argv[]) {
|
|||
CoreModule::start();
|
||||
IFModule::start();
|
||||
MultimediaModule::start();
|
||||
HTMLModule::start();
|
||||
IndexModule::start();
|
||||
ArchModule::start();
|
||||
InterModule::start();
|
||||
|
@ -73,6 +74,7 @@ int Main::core_inform_main(int argc, char *argv[]) {
|
|||
CoreModule::end();
|
||||
IFModule::end();
|
||||
IndexModule::end();
|
||||
HTMLModule::end();
|
||||
InterModule::end();
|
||||
ArchModule::end();
|
||||
BuildingModule::end();
|
||||
|
|
|
@ -16,6 +16,7 @@ Import: syntax
|
|||
Import: problems
|
||||
Import: linguistics
|
||||
Import: kinds
|
||||
Import: inbuild/html
|
||||
Import: inbuild/arch
|
||||
Import: inter/inter
|
||||
Import: inbuild/inbuild
|
||||
|
|
|
@ -71,9 +71,9 @@
|
|||
mkdir: $PATH/_Results_Ideal
|
||||
|
||||
if: $CASE PM_Map%c*
|
||||
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
else
|
||||
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
|
||||
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
|
||||
endif
|
||||
|
||||
if: $VM G
|
||||
|
@ -191,9 +191,9 @@
|
|||
mkdir: $PATH/_Results_Ideal
|
||||
|
||||
if: $CASE PM_Map%c*
|
||||
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
else
|
||||
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
|
||||
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
|
||||
endif
|
||||
|
||||
if: $VM G
|
||||
|
@ -273,7 +273,7 @@
|
|||
set: $VM = G
|
||||
extract: $WORK/Example.inform/Source/story.ni $VM
|
||||
|
||||
set: $I7OPTIONS = -fixtime -format=ulx -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7OPTIONS = -fixtime -format=ulx -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7CONSOLE = $WORK/Example.inform/Build/i7_output.txt
|
||||
|
||||
mkdir: $WORK/Transient
|
||||
|
@ -370,7 +370,7 @@
|
|||
mkdir: $PATH/_Maps_Actual
|
||||
mkdir: $PATH/_Maps_Ideal
|
||||
|
||||
set: $I7OPTIONS = -format=ulx -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7OPTIONS = -format=ulx -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
|
||||
set: $I7CONSOLE = $WORK/Example.inform/Build/i7_output.txt
|
||||
|
||||
step: $I7 `$I7OPTIONS -project $WORK/Example.inform >$I7CONSOLE 2>&1
|
||||
|
@ -422,7 +422,7 @@
|
|||
copy: $WORKSPACE/Samples/Powermac.aiff $WORK/Example.materials/Sounds/Powermac.aiff
|
||||
endif
|
||||
|
||||
set: $I7OPTIONS = -fixtime -format=$FORMAT -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL -release
|
||||
set: $I7OPTIONS = -fixtime -format=$FORMAT -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL -release
|
||||
|
||||
mkdir: $WORK/Transient
|
||||
step: find $WORK/Transient -mindepth 1 -delete
|
||||
|
|
|
@ -36,8 +36,6 @@ We need to itemise the structures we'll want to allocate:
|
|||
@e pcalc_prop_deferral_MT
|
||||
@e literal_pattern_MT
|
||||
@e generalisation_MT
|
||||
@e extension_dictionary_entry_MT
|
||||
@e known_extension_clash_MT
|
||||
@e i6_schema_array_MT
|
||||
@e list_together_routine_MT
|
||||
@e past_tense_condition_record_MT
|
||||
|
@ -113,7 +111,6 @@ ALLOCATE_INDIVIDUALLY(dval_written)
|
|||
ALLOCATE_INDIVIDUALLY(equation_node)
|
||||
ALLOCATE_INDIVIDUALLY(equation_symbol)
|
||||
ALLOCATE_INDIVIDUALLY(equation)
|
||||
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
|
||||
ALLOCATE_INDIVIDUALLY(generalisation)
|
||||
ALLOCATE_INDIVIDUALLY(heading)
|
||||
ALLOCATE_INDIVIDUALLY(i6_inclusion_matter)
|
||||
|
@ -125,7 +122,6 @@ ALLOCATE_INDIVIDUALLY(instance)
|
|||
ALLOCATE_INDIVIDUALLY(internal_test_case)
|
||||
ALLOCATE_INDIVIDUALLY(inv_token_problem_token)
|
||||
ALLOCATE_INDIVIDUALLY(kind_interaction)
|
||||
ALLOCATE_INDIVIDUALLY(known_extension_clash)
|
||||
ALLOCATE_INDIVIDUALLY(list_together_routine)
|
||||
ALLOCATE_INDIVIDUALLY(literal_list)
|
||||
ALLOCATE_INDIVIDUALLY(literal_pattern_name)
|
||||
|
|
|
@ -10,7 +10,6 @@ options with inbuild: see that module for more.
|
|||
=
|
||||
int existing_story_file = FALSE; /* Ignore source text to blorb existing story file? */
|
||||
int rng_seed_at_start_of_play = 0; /* The seed value, or 0 if not seeded */
|
||||
int census_mode = FALSE; /* Inform running only to update extension documentation */
|
||||
int show_progress_indicator = TRUE; /* Produce percentage of progress messages */
|
||||
int scoring_option_set = NOT_APPLICABLE; /* Whether in this case a score is kept at run time */
|
||||
int disable_import = FALSE;
|
||||
|
@ -40,7 +39,6 @@ int model_world_constructed = FALSE; /* World model is now constructed */
|
|||
int indexing_stage = FALSE; /* Everything is done except indexing */
|
||||
|
||||
@ =
|
||||
int report_clock_time = FALSE;
|
||||
time_t right_now;
|
||||
text_stream *inter_processing_file = NULL;
|
||||
text_stream *inter_processing_pipeline = NULL;
|
||||
|
@ -48,7 +46,6 @@ dictionary *pipeline_vars = NULL;
|
|||
pathname *path_to_inform7 = NULL;
|
||||
|
||||
int CoreMain::main(int argc, char *argv[]) {
|
||||
clock_t start = clock();
|
||||
@<Banner and startup@>;
|
||||
@<Register command-line arguments@>;
|
||||
int proceed = CommandLine::read(argc, argv, NULL, &CoreMain::switch, &CoreMain::bareword);
|
||||
|
@ -57,23 +54,10 @@ int CoreMain::main(int argc, char *argv[]) {
|
|||
@<With that done, configure all other settings@>;
|
||||
@<Open the debugging log and the problems report@>;
|
||||
@<Boot up the compiler@>;
|
||||
if (census_mode)
|
||||
Extensions::Files::handle_census_mode();
|
||||
else {
|
||||
@<Work out our kit requirements@>;
|
||||
@<Perform lexical analysis@>;
|
||||
@<Perform semantic analysis@>;
|
||||
@<Read the assertions in two passes@>;
|
||||
@<Make the model world@>;
|
||||
@<Tables and grammar@>;
|
||||
@<Phrases and rules@>;
|
||||
@<Generate inter@>;
|
||||
@<Convert inter to Inform 6@>;
|
||||
@<Generate metadata@>;
|
||||
@<Post mortem logging@>;
|
||||
}
|
||||
Inbuild::go_operational();
|
||||
inform_project *project = Inbuild::project();
|
||||
if (project) CoreMain::task(project);
|
||||
}
|
||||
clock_t end = clock();
|
||||
@<Shutdown and rennab@>;
|
||||
if (problem_count > 0) Problems::Fatal::exit(1);
|
||||
return 0;
|
||||
|
@ -94,8 +78,6 @@ arguments in order to set certain pathnames or filenames, so the following
|
|||
list is not exhaustive.
|
||||
|
||||
@e CASE_CLSW
|
||||
@e CENSUS_CLSW
|
||||
@e CLOCK_CLSW
|
||||
@e CRASHALL_CLSW
|
||||
@e NOINDEX_CLSW
|
||||
@e NOPROGRESS_CLSW
|
||||
|
@ -111,10 +93,6 @@ list is not exhaustive.
|
|||
L"inform7: a compiler from source text to Inform 6 code\n\n"
|
||||
L"Usage: inform7 [OPTIONS] [SOURCETEXT]\n");
|
||||
|
||||
CommandLine::declare_boolean_switch(CENSUS_CLSW, L"census", 1,
|
||||
L"perform an extensions census (rather than compile)");
|
||||
CommandLine::declare_boolean_switch(CLOCK_CLSW, L"clock", 1,
|
||||
L"time how long inform7 takes to run");
|
||||
CommandLine::declare_boolean_switch(CRASHALL_CLSW, L"crash-all", 1,
|
||||
L"crash intentionally on Problem messages (for debugger backtraces)");
|
||||
CommandLine::declare_boolean_switch(NOINDEX_CLSW, L"noindex", 1,
|
||||
|
@ -142,7 +120,7 @@ list is not exhaustive.
|
|||
|
||||
@<With that done, configure all other settings@> =
|
||||
Inbuild::optioneering_complete(NULL);
|
||||
if (Locations::set_defaults(census_mode) == FALSE)
|
||||
if (Locations::set_defaults() == FALSE)
|
||||
Problems::Fatal::issue("Unable to create folders in local file system");
|
||||
Log::set_debug_log_filename(filename_of_debugging_log);
|
||||
NaturalLanguages::default_to_English();
|
||||
|
@ -155,6 +133,24 @@ list is not exhaustive.
|
|||
CommandLine::play_back_log();
|
||||
Problems::Issue::start_problems_report();
|
||||
|
||||
@ =
|
||||
void CoreMain::task(inform_project *project) {
|
||||
clock_t start = clock();
|
||||
@<Perform lexical analysis@>;
|
||||
@<Perform semantic analysis@>;
|
||||
@<Read the assertions in two passes@>;
|
||||
@<Make the model world@>;
|
||||
@<Tables and grammar@>;
|
||||
@<Phrases and rules@>;
|
||||
@<Generate inter@>;
|
||||
@<Convert inter to Inform 6@>;
|
||||
@<Generate metadata@>;
|
||||
@<Post mortem logging@>;
|
||||
clock_t end = clock();
|
||||
int cpu_time_used = ((int) (end - start)) / (CLOCKS_PER_SEC/100);
|
||||
LOG("CPU time: %d centiseconds\n", cpu_time_used);
|
||||
}
|
||||
|
||||
@
|
||||
|
||||
@d COMPILATION_STEP(routine, mark) {
|
||||
|
@ -183,9 +179,6 @@ list is not exhaustive.
|
|||
COMPILATION_STEP(Index::DocReferences::read_xrefs, I"Index::DocReferences::read_xrefs")
|
||||
doc_references_top = lexer_wordcount - 1;
|
||||
|
||||
@<Work out our kit requirements@> =
|
||||
Inbuild::go_operational();
|
||||
|
||||
@<Perform lexical analysis@> =
|
||||
ProgressBar::update_progress_bar(0, 0);
|
||||
if (problem_count == 0) CoreMain::go_to_log_phase(I"Lexical analysis");
|
||||
|
@ -448,14 +441,9 @@ with "Output.i6t".
|
|||
Problems::write_reports(FALSE);
|
||||
|
||||
LOG("Total of %d files written as streams.\n", total_file_writes);
|
||||
int cpu_time_used = ((int) (end - start)) / (CLOCKS_PER_SEC/100);
|
||||
LOG("CPU time: %d centiseconds\n", cpu_time_used);
|
||||
Writers::log_escape_usage();
|
||||
|
||||
WRITE_TO(STDOUT, "%s has finished", HUMAN_READABLE_INTOOL_NAME);
|
||||
if (report_clock_time)
|
||||
WRITE_TO(STDOUT, ": %d centiseconds used", cpu_time_used);
|
||||
WRITE_TO(STDOUT, ".\n");
|
||||
WRITE_TO(STDOUT, "%s has finished.\n", HUMAN_READABLE_INTOOL_NAME);
|
||||
}
|
||||
|
||||
|
||||
|
@ -473,8 +461,6 @@ void CoreMain::go_to_log_phase(text_stream *argument) {
|
|||
void CoreMain::switch(int id, int val, text_stream *arg, void *state) {
|
||||
switch (id) {
|
||||
/* Miscellaneous boolean settings */
|
||||
case CENSUS_CLSW: census_mode = val; break;
|
||||
case CLOCK_CLSW: report_clock_time = val; break;
|
||||
case CRASHALL_CLSW: debugger_mode = val; crash_on_all_errors = val; break;
|
||||
case NOINDEX_CLSW: do_not_generate_index = val; break;
|
||||
case NOPROGRESS_CLSW: show_progress_indicator = val?FALSE:TRUE; break;
|
||||
|
@ -508,10 +494,6 @@ void CoreMain::switch(int id, int val, text_stream *arg, void *state) {
|
|||
Inbuild::option(id, val, arg, state);
|
||||
}
|
||||
|
||||
int CoreMain::census_mode(void) {
|
||||
return census_mode;
|
||||
}
|
||||
|
||||
void CoreMain::bareword(int id, text_stream *opt, void *state) {
|
||||
if (Inbuild::set_I7_source(opt) == FALSE)
|
||||
Errors::fatal_with_text("unknown command line argument: %S (see -help)", opt);
|
||||
|
|
|
@ -11,8 +11,6 @@ these are stored in the following globals. Explanations are given below,
|
|||
not here.
|
||||
|
||||
= (early code)
|
||||
pathname *pathname_of_extension_docs = NULL;
|
||||
pathname *pathname_of_extension_docs_inner = NULL;
|
||||
pathname *pathname_of_HTML_models = NULL;
|
||||
pathname *pathname_of_materials_figures = NULL;
|
||||
pathname *pathname_of_materials_release = NULL;
|
||||
|
@ -22,7 +20,6 @@ pathname *pathname_of_project_index_folder = NULL;
|
|||
pathname *pathname_of_released_figures = NULL;
|
||||
pathname *pathname_of_released_interpreter = NULL;
|
||||
pathname *pathname_of_released_sounds = NULL;
|
||||
pathname *pathname_of_transient_census_data = NULL;
|
||||
|
||||
@ And secondly, the files:
|
||||
|
||||
|
@ -35,7 +32,6 @@ filename *filename_of_debugging_log = NULL;
|
|||
filename *filename_of_documentation_snippets = NULL;
|
||||
filename *filename_of_epsfile = NULL;
|
||||
filename *filename_of_existing_story_file = NULL;
|
||||
filename *filename_of_extensions_dictionary = NULL;
|
||||
filename *filename_of_headings = NULL;
|
||||
filename *filename_of_ifiction_record = NULL;
|
||||
filename *filename_of_intro_booklet = NULL;
|
||||
|
@ -66,16 +62,11 @@ Inform can run in two modes: regular mode, when it's compiling source text,
|
|||
and census mode, when it's scanning the file system for extensions.
|
||||
|
||||
=
|
||||
int Locations::set_defaults(int census_mode) {
|
||||
int Locations::set_defaults(void) {
|
||||
@<Internal resources@>;
|
||||
@<External resources@>;
|
||||
@<Project resources@>;
|
||||
@<Materials resources@>;
|
||||
inform_project *project = Inbuild::project();
|
||||
if ((census_mode == FALSE) && (project == NULL))
|
||||
Problems::Fatal::issue("Except in census mode, source text must be supplied");
|
||||
if ((census_mode) && (project))
|
||||
Problems::Fatal::issue("In census mode, no source text may be supplied");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -152,30 +143,8 @@ not distinguish between permanent and transient external resources.
|
|||
|
||||
pathname *pathname_of_transient_external_resources = Inbuild::transient();
|
||||
if (Pathnames::create_in_file_system(pathname_of_transient_external_resources) == 0) return FALSE;
|
||||
@<Transient documentation@>;
|
||||
@<Transient telemetry@>;
|
||||
|
||||
@ The documentation folder is in effect a little website of its own, generated
|
||||
automatically by Inform. There'll be some files at the top level, and then
|
||||
there are files on each extension, in suitable subfolders. The census data
|
||||
subfolder is not browsable or linked to, but holds working files needed when
|
||||
assembling all this.
|
||||
|
||||
@<Transient documentation@> =
|
||||
pathname_of_extension_docs =
|
||||
Pathnames::subfolder(pathname_of_transient_external_resources, I"Documentation");
|
||||
if (Pathnames::create_in_file_system(pathname_of_extension_docs) == 0) return FALSE;
|
||||
|
||||
pathname_of_transient_census_data =
|
||||
Pathnames::subfolder(pathname_of_extension_docs, I"Census");
|
||||
if (Pathnames::create_in_file_system(pathname_of_transient_census_data) == 0) return FALSE;
|
||||
filename_of_extensions_dictionary =
|
||||
Filenames::in_folder(pathname_of_transient_census_data, I"Dictionary.txt");
|
||||
|
||||
pathname_of_extension_docs_inner =
|
||||
Pathnames::subfolder(pathname_of_extension_docs, I"Extensions");
|
||||
if (Pathnames::create_in_file_system(pathname_of_extension_docs_inner) == 0) return FALSE;
|
||||
|
||||
@ Telemetry is not as sinister as it sounds: the app isn't sending data out
|
||||
on the Internet, only (if requested) logging what it's doing to a local file.
|
||||
This was provided for classroom use, so that teachers can see what their
|
||||
|
@ -225,7 +194,7 @@ during the compilation process. The opening part here may be a surprise:
|
|||
In extension census mode, Inform is running not to compile something but to
|
||||
extract details of all the extensions installed. But it still needs somewhere
|
||||
to write its temporary and debugging files, and there is no project bundle
|
||||
to write into. To get round this, we use the census data area as if it
|
||||
to write into. To get round this, we use the transient data area as if it
|
||||
were indeed a project bundle.
|
||||
|
||||
Briefly: we aim to compile the source text to an Inform 6 program; we issue
|
||||
|
@ -236,12 +205,8 @@ called; and similarly for the report which the releasing tool Inblorb
|
|||
will produce if this is a Release run.
|
||||
|
||||
@<The Build folder within the project@> =
|
||||
pathname *build_folder = pathname_of_transient_census_data;
|
||||
|
||||
if (census_mode == FALSE) {
|
||||
build_folder = Pathnames::subfolder(proj, I"Build");
|
||||
if (Pathnames::create_in_file_system(build_folder) == 0) return FALSE;
|
||||
}
|
||||
pathname *build_folder = Pathnames::subfolder(proj, I"Build");
|
||||
if (Pathnames::create_in_file_system(build_folder) == 0) return FALSE;
|
||||
|
||||
filename_of_report = Filenames::in_folder(build_folder, I"Problems.html");
|
||||
filename_of_debugging_log = Filenames::in_folder(build_folder, I"Debug log.txt");
|
||||
|
@ -271,10 +236,9 @@ the index as seen by the user.
|
|||
pathname_of_project_index_details_folder =
|
||||
Pathnames::subfolder(pathname_of_project_index_folder, I"Details");
|
||||
|
||||
if (census_mode == FALSE)
|
||||
if ((Pathnames::create_in_file_system(pathname_of_project_index_folder) == 0) ||
|
||||
(Pathnames::create_in_file_system(pathname_of_project_index_details_folder) == 0))
|
||||
return FALSE;
|
||||
if ((Pathnames::create_in_file_system(pathname_of_project_index_folder) == 0) ||
|
||||
(Pathnames::create_in_file_system(pathname_of_project_index_details_folder) == 0))
|
||||
return FALSE;
|
||||
|
||||
filename_of_headings =
|
||||
Filenames::in_folder(pathname_of_project_index_folder, I"Headings.xml");
|
||||
|
@ -360,18 +324,6 @@ filename *Locations::of_extension(pathname *E, text_stream *title, text_stream *
|
|||
return F;
|
||||
}
|
||||
|
||||
@ Documentation is similarly arranged:
|
||||
|
||||
=
|
||||
filename *Locations::of_extension_documentation(text_stream *title, text_stream *author) {
|
||||
TEMPORARY_TEXT(leaf);
|
||||
WRITE_TO(leaf, "%S.html", title);
|
||||
filename *F = Filenames::in_folder(
|
||||
Pathnames::subfolder(pathname_of_extension_docs_inner, author), leaf);
|
||||
DISCARD_TEXT(leaf);
|
||||
return F;
|
||||
}
|
||||
|
||||
@h Location of index files.
|
||||
Filenames within the |Index| subfolder. Filenames in |Details| have the form
|
||||
|N_S| where |N| is the integer supplied and |S| the leafname; for instance,
|
||||
|
|
|
@ -361,122 +361,177 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, inform_extension *f
|
|||
}
|
||||
}
|
||||
|
||||
@h Updating the documentation.
|
||||
This is done in the course of taking an extension census, which is called
|
||||
for in one of two circumstances: when Inform is being run in "census mode" to
|
||||
notify it that extensions have been installed or uninstalled; or when Inform
|
||||
has completed the successful compilation of a source text. In the latter
|
||||
case, it knows quite a lot about the extensions actually used in that
|
||||
compilation, and so can write detailed versions of their documentation:
|
||||
since it is updating extension documentation anyway, it conducts a census
|
||||
as well. (In both cases the extension dictionary is also worked upon.) The
|
||||
two alternatives are expressed here:
|
||||
@ Nothing can prevent a certain repetitiousness intruding here, but there is
|
||||
just enough local knowledge required to make it foolhardy to try to automate
|
||||
this from a dump of the excerpt meanings table (say). The ordering of
|
||||
paragraphs, as in Roget's Thesaurus, tries to proceed from solid things
|
||||
through to diffuse linguistic ones. But the reader of the resulting
|
||||
documentation page could be forgiven for thinking it a miscellany.
|
||||
|
||||
=
|
||||
void Extensions::Files::handle_census_mode(void) {
|
||||
if (census_mode) {
|
||||
extension_census *C = Extensions::Census::new();
|
||||
Extensions::Dictionary::load();
|
||||
Extensions::Census::perform(C);
|
||||
Extensions::Files::write_top_level_of_extensions_documentation(C);
|
||||
Extensions::Files::write_sketchy_documentation_for_extensions_found();
|
||||
void Extensions::Files::document_in_detail(OUTPUT_STREAM, inform_extension *E) {
|
||||
Extensions::Dictionary::erase_entries(E);
|
||||
if (E) Extensions::Dictionary::time_stamp(E);
|
||||
|
||||
@<Document and dictionary the kinds made in extension@>;
|
||||
@<Document and dictionary the objects made in extension@>;
|
||||
|
||||
@<Document and dictionary the global variables made in extension@>;
|
||||
@<Document and dictionary the enumerated constant values made in extension@>;
|
||||
|
||||
@<Document and dictionary the kinds of action made in extension@>;
|
||||
@<Document and dictionary the actions made in extension@>;
|
||||
|
||||
@<Document and dictionary the verbs made in extension@>;
|
||||
@<Document and dictionary the adjectival phrases made in extension@>;
|
||||
@<Document and dictionary the property names made in extension@>;
|
||||
|
||||
@<Document and dictionary the use options made in extension@>;
|
||||
}
|
||||
|
||||
@ Off we go, then. Kinds of object:
|
||||
|
||||
@<Document and dictionary the kinds made in extension@> =
|
||||
kind *K;
|
||||
int kc = 0;
|
||||
LOOP_OVER_BASE_KINDS(K) {
|
||||
parse_node *S = Kinds::Behaviour::get_creating_sentence(K);
|
||||
if (S) {
|
||||
if (Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(S))) == E->read_into_file) {
|
||||
wording W = Kinds::Behaviour::get_name(K, FALSE);
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Kinds", I"kind", W);
|
||||
kind *S = Kinds::Compare::super(K);
|
||||
if (S) {
|
||||
W = Kinds::Behaviour::get_name(S, FALSE);
|
||||
if (Wordings::nonempty(W)) WRITE(" (a kind of %+W)", W);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
void Extensions::Files::update_census(void) {
|
||||
Extensions::Dictionary::load();
|
||||
extension_census *C = Extensions::Census::new();
|
||||
Extensions::Census::perform(C);
|
||||
Extensions::Files::write_top_level_of_extensions_documentation(C);
|
||||
inform_extension *E;
|
||||
LOOP_OVER(E, inform_extension) Extensions::Documentation::write_detailed(E);
|
||||
Extensions::Files::write_sketchy_documentation_for_extensions_found();
|
||||
Extensions::Dictionary::write_back();
|
||||
if (Log::aspect_switched_on(EXTENSIONS_CENSUS_DA)) Works::log_work_hash_table();
|
||||
}
|
||||
@ Actual objects:
|
||||
|
||||
@ Documenting extensions seen but not used: we run through the census
|
||||
results in no particular order and create a sketchy page of documentation,
|
||||
if there's no better one already.
|
||||
|
||||
=
|
||||
void Extensions::Files::write_sketchy_documentation_for_extensions_found(void) {
|
||||
extension_census_datum *ecd;
|
||||
LOOP_OVER(ecd, extension_census_datum)
|
||||
Extensions::Documentation::write_sketchy(ecd);
|
||||
}
|
||||
|
||||
@h Writing the extensions home pages.
|
||||
Extensions documentation forms a mini-website within the Inform
|
||||
documentation. There is a top level consisting of two home pages: a
|
||||
directory of all installed extensions, and an index to the terms defined in
|
||||
those extensions. A cross-link switches between them. Each of these links
|
||||
down to the bottom level, where there is a page for every installed
|
||||
extension (wherever it is installed). The picture is therefore something
|
||||
like this:
|
||||
|
||||
= (not code)
|
||||
(Main documentation contents page)
|
||||
|
|
||||
Extensions.html--ExtIndex.html
|
||||
| \/ |
|
||||
| /\ |
|
||||
Nigel Toad/Eggs Barnabas Dundritch/Neopolitan Iced Cream ...
|
||||
|
||||
@ These pages are stored at the relative pathnames
|
||||
|
||||
|Extensions/Documentation/Extensions.html|
|
||||
|Extensions/Documentation/ExtIndex.html|
|
||||
|
||||
They are made by inserting content in place of the material between the
|
||||
HTML anchors |on| and |off| in a template version of the page built in
|
||||
to the application, with a leafname which varies from platform to
|
||||
platform, for reasons as always to do with the vagaries of Internet
|
||||
Explorer 7 for Windows.
|
||||
|
||||
=
|
||||
void Extensions::Files::write_top_level_of_extensions_documentation(extension_census *C) {
|
||||
Extensions::Files::write_top_level_extensions_page(I"Extensions.html", 1, C);
|
||||
Extensions::Files::write_top_level_extensions_page(I"ExtIndex.html", 2, NULL);
|
||||
}
|
||||
|
||||
@ =
|
||||
void Extensions::Files::write_top_level_extensions_page(text_stream *leaf, int content, extension_census *C) {
|
||||
text_stream HOMEPAGE_struct;
|
||||
text_stream *OUT = &HOMEPAGE_struct;
|
||||
filename *F = Filenames::in_folder(pathname_of_extension_docs, leaf);
|
||||
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE)
|
||||
Problems::Fatal::filename_related(
|
||||
"Unable to open extensions documentation index for writing", F);
|
||||
HTML::declare_as_HTML(OUT, FALSE);
|
||||
|
||||
HTML::begin_head(OUT, NULL);
|
||||
HTML::title(OUT, I"Extensions");
|
||||
HTML::incorporate_javascript(OUT, TRUE,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"extensions.js"));
|
||||
HTML::incorporate_CSS(OUT,
|
||||
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
|
||||
HTML::end_head(OUT);
|
||||
|
||||
HTML::begin_body(OUT, NULL);
|
||||
HTML::begin_html_table(OUT, NULL, TRUE, 0, 4, 0, 0, 0);
|
||||
HTML::first_html_column(OUT, 0);
|
||||
HTML_TAG_WITH("img", "src='inform:/doc_images/extensions@2x.png' border=0 width=150 height=150");
|
||||
HTML::next_html_column(OUT, 0);
|
||||
|
||||
HTML_OPEN_WITH("div", "class=\"headingboxDark\"");
|
||||
HTML_OPEN_WITH("div", "class=\"headingtextWhite\"");
|
||||
WRITE("Installed Extensions");
|
||||
HTML_CLOSE("div");
|
||||
HTML_OPEN_WITH("div", "class=\"headingrubricWhite\"");
|
||||
WRITE("Bundles of extra rules or phrases to extend what Inform can do");
|
||||
HTML_CLOSE("div");
|
||||
HTML_CLOSE("div");
|
||||
|
||||
switch (content) {
|
||||
case 1: Extensions::Census::write_results(OUT, C); break;
|
||||
case 2: Extensions::Dictionary::write_to_HTML(OUT); break;
|
||||
@<Document and dictionary the objects made in extension@> =
|
||||
instance *I;
|
||||
int kc = 0;
|
||||
LOOP_OVER_OBJECT_INSTANCES(I) {
|
||||
wording OW = Instances::get_name(I, FALSE);
|
||||
if ((Instances::get_creating_sentence(I)) && (Wordings::nonempty(OW))) {
|
||||
if (Lexer::file_of_origin(
|
||||
Wordings::first_wn(ParseTree::get_text(Instances::get_creating_sentence(I))))
|
||||
== E->read_into_file) {
|
||||
TEMPORARY_TEXT(name_of_its_kind);
|
||||
kind *k = Instances::to_kind(I);
|
||||
wording W = Kinds::Behaviour::get_name(k, FALSE);
|
||||
WRITE_TO(name_of_its_kind, "%+W", W);
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E,
|
||||
"Physical creations", name_of_its_kind, OW);
|
||||
WRITE(" (a %S)", name_of_its_kind);
|
||||
DISCARD_TEXT(name_of_its_kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
HTML::end_body(OUT);
|
||||
@ Global variables:
|
||||
|
||||
@<Document and dictionary the global variables made in extension@> =
|
||||
nonlocal_variable *q;
|
||||
int kc = 0;
|
||||
LOOP_OVER(q, nonlocal_variable)
|
||||
if ((Wordings::first_wn(q->name) >= 0) &&
|
||||
(NonlocalVariables::is_global(q)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(q->name)) == E->read_into_file) &&
|
||||
(Sentences::Headings::indexed(Sentences::Headings::of_wording(q->name)))) {
|
||||
if (<value-understood-variable-name>(q->name) == FALSE)
|
||||
kc = Extensions::Files::document_headword(OUT,
|
||||
kc, E, "Values that vary", I"value", q->name);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Constants:
|
||||
|
||||
@<Document and dictionary the enumerated constant values made in extension@> =
|
||||
instance *q;
|
||||
int kc = 0;
|
||||
LOOP_OVER_ENUMERATION_INSTANCES(q) {
|
||||
wording NW = Instances::get_name(q, FALSE);
|
||||
if ((Wordings::nonempty(NW)) && (Lexer::file_of_origin(Wordings::first_wn(NW)) == E->read_into_file))
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Values", I"value", NW);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Kinds of action:
|
||||
|
||||
@<Document and dictionary the kinds of action made in extension@> =
|
||||
#ifdef IF_MODULE
|
||||
PL::Actions::Patterns::Named::index_for_extension(OUT, E->read_into_file, E);
|
||||
#endif
|
||||
|
||||
@ Actions:
|
||||
|
||||
@<Document and dictionary the actions made in extension@> =
|
||||
#ifdef IF_MODULE
|
||||
PL::Actions::Index::index_for_extension(OUT, E->read_into_file, E);
|
||||
#endif
|
||||
|
||||
@ Verbs (this one we delegate):
|
||||
|
||||
@<Document and dictionary the verbs made in extension@> =
|
||||
Index::Lexicon::list_verbs_in_file(OUT, E->read_into_file, E);
|
||||
|
||||
@ Adjectival phrases:
|
||||
|
||||
@<Document and dictionary the adjectival phrases made in extension@> =
|
||||
adjectival_phrase *adj;
|
||||
int kc = 0;
|
||||
LOOP_OVER(adj, adjectival_phrase) {
|
||||
wording W = Adjectives::get_text(adj, FALSE);
|
||||
if ((Wordings::nonempty(W)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(W)) == E->read_into_file))
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Adjectives", I"adjective", W);
|
||||
}
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Other adjectives:
|
||||
|
||||
@<Document and dictionary the property names made in extension@> =
|
||||
property *prn;
|
||||
int kc = 0;
|
||||
LOOP_OVER(prn, property)
|
||||
if ((Wordings::nonempty(prn->name)) &&
|
||||
(Properties::is_shown_in_index(prn)) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(prn->name)) == E->read_into_file))
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Properties", I"property",
|
||||
prn->name);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Use options:
|
||||
|
||||
@<Document and dictionary the use options made in extension@> =
|
||||
use_option *uo;
|
||||
int kc = 0;
|
||||
LOOP_OVER(uo, use_option)
|
||||
if ((Wordings::first_wn(uo->name) >= 0) &&
|
||||
(Lexer::file_of_origin(Wordings::first_wn(uo->name)) == E->read_into_file))
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Use options", I"use option",
|
||||
uo->name);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
|
||||
@ Finally, the utility routine which keeps count (hence |kc|) and displays
|
||||
suitable lists, while entering each entry in turn into the extension
|
||||
dictionary.
|
||||
|
||||
=
|
||||
int Extensions::Files::document_headword(OUTPUT_STREAM, int kc, inform_extension *E, char *par_heading,
|
||||
text_stream *category, wording W) {
|
||||
if (kc++ == 0) { HTML_OPEN("p"); WRITE("%s: ", par_heading); }
|
||||
else WRITE(", ");
|
||||
WRITE("<b>%+W</b>", W);
|
||||
Extensions::Dictionary::new_entry(category, E, W);
|
||||
return kc;
|
||||
}
|
||||
|
||||
@ And that at last brings us to a milestone: the end of the Land of Extensions.
|
||||
We can return to Inform's more usual concerns.
|
||||
|
|
|
@ -69,8 +69,6 @@ which, we also handle extension installation, uninstallation and
|
|||
documentation here."
|
||||
Extension Files
|
||||
Including Extensions
|
||||
Extension Dictionary
|
||||
Extension Documentation
|
||||
|
||||
Chapter 9: The A-Parser
|
||||
"We work through the assertion sentences in the parse tree one by one, and
|
||||
|
|
|
@ -171,7 +171,7 @@ int PL::Bibliographic::story_author_is(text_stream *p) {
|
|||
TEMPORARY_TEXT(TEMP);
|
||||
wording W = ParseTree::get_text(spec);
|
||||
int w1 = Wordings::first_wn(W);
|
||||
HTMLFiles::compile_bibliographic_text(TEMP, Lexer::word_text(w1));
|
||||
PL::Bibliographic::compile_bibliographic_text(TEMP, Lexer::word_text(w1));
|
||||
if (Str::eq(TEMP, p)) result = TRUE;
|
||||
DISCARD_TEXT(TEMP);
|
||||
return result;
|
||||
|
@ -400,9 +400,195 @@ void PL::Bibliographic::index_bibliographic_variable(OUTPUT_STREAM, nonlocal_var
|
|||
COMPILATION_MODE_ENTER(COMPILE_TEXT_TO_XML_CMODE);
|
||||
if ((nlv) && (NonlocalVariables::has_initial_value_set(nlv))) {
|
||||
wording W = NonlocalVariables::treat_as_plain_text_word(nlv);
|
||||
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(Wordings::first_wn(W)));
|
||||
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(Wordings::first_wn(W)));
|
||||
} else {
|
||||
WRITE("%S", t);
|
||||
}
|
||||
END_COMPILATION_MODE;
|
||||
}
|
||||
|
||||
@h Bibliographic text.
|
||||
"Bibliographic text" is text used in bibliographic data about the work
|
||||
of IF compiled: for instance, in the iFiction record, or in the Library
|
||||
Card section of the HTML index. Note that the exact output format depends
|
||||
on global variables, which allow the bibliographic text writing code to
|
||||
configure NI for its current purposes. On non-empty strings this routine
|
||||
therefore splits into one of three independent methods.
|
||||
|
||||
=
|
||||
void PL::Bibliographic::compile_bibliographic_text(OUTPUT_STREAM, wchar_t *p) {
|
||||
if (p == NULL) return;
|
||||
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_XML_CMODE))
|
||||
@<Compile bibliographic text as XML respecting Treaty of Babel rules@>;
|
||||
if (TEST_COMPILATION_MODE(TRUNCATE_TEXT_CMODE))
|
||||
@<Compile bibliographic text as a truncated filename@>;
|
||||
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_I6_CMODE))
|
||||
@<Compile bibliographic text as an I6 string@>
|
||||
@<Compile bibliographic text as HTML@>;
|
||||
}
|
||||
|
||||
@ This looks like a standard routine for converting ISO Latin-1 to UTF-8
|
||||
with XML escapes, but there are a few conventions on whitespace, too, in order
|
||||
to comply with a strict reading of the Treaty of Babel. (This is intended
|
||||
for fields in iFiction records.)
|
||||
|
||||
@<Compile bibliographic text as XML respecting Treaty of Babel rules@> =
|
||||
int i = 0, i2 = Wide::len(p)-1, snl, wsc;
|
||||
if ((p[0] == '"') && (p[i2] == '"')) { i++; i2--; } /* omit surrounding double-quotes */
|
||||
while (Characters::is_babel_whitespace(p[i])) i++; /* omit leading whitespace */
|
||||
while ((i2>=0) && (Characters::is_babel_whitespace(p[i2]))) i2--; /* omit trailing whitespace */
|
||||
for (snl = FALSE, wsc = 0; i<=i2; i++) {
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t':
|
||||
snl = FALSE;
|
||||
wsc++;
|
||||
int k = i;
|
||||
while ((p[k] == ' ') || (p[k] == '\x0a') || (p[k] == '\x0d') || (p[k] == '\t')) k++;
|
||||
if ((wsc == 1) && (p[k] != NEWLINE_IN_STRING)) WRITE(" ");
|
||||
break;
|
||||
case NEWLINE_IN_STRING:
|
||||
if (snl) break;
|
||||
WRITE("<br/>");
|
||||
snl = TRUE; wsc = 1; break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
WRITE("'"); break;
|
||||
}
|
||||
int n = CompiledText::expand_unisub(OUT, p, i);
|
||||
if (n >= 0) { i = n; break; }
|
||||
/* and otherwise fall through to the default case */
|
||||
default:
|
||||
snl = FALSE;
|
||||
wsc = 0;
|
||||
switch(p[i]) {
|
||||
case '&': WRITE("&"); break;
|
||||
case '<': WRITE("<"); break;
|
||||
case '>': WRITE(">"); break;
|
||||
default: PUT(p[i]); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ In the HTML version, we want to respect the forcing of newlines, and
|
||||
also the |[']| escape to obtain a literal single quotation mark.
|
||||
|
||||
@<Compile bibliographic text as HTML@> =
|
||||
int i, whitespace_count=0;
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t':
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case NEWLINE_IN_STRING:
|
||||
while (p[i+1] == NEWLINE_IN_STRING) i++;
|
||||
PUT('<');
|
||||
PUT('p');
|
||||
PUT('>');
|
||||
whitespace_count = 1;
|
||||
break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
PUT('\''); break;
|
||||
}
|
||||
int n = CompiledText::expand_unisub(OUT, p, i);
|
||||
if (n >= 0) { i = n; break; }
|
||||
/* and otherwise fall through to the default case */
|
||||
default:
|
||||
whitespace_count = 0;
|
||||
PUT(p[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ In the Inform 6 string version, we suppress the forcing of newlines, but
|
||||
otherwise it's much the same.
|
||||
|
||||
@<Compile bibliographic text as an I6 string@> =
|
||||
int i, whitespace_count=0;
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case '[':
|
||||
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
|
||||
i += 2;
|
||||
PUT('\''); break;
|
||||
} /* and otherwise fall through to the default case */
|
||||
default:
|
||||
whitespace_count = 0;
|
||||
PUT(p[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@ This code is used to work out a good filename for something given a name
|
||||
inside NI. For instance, if a project is called
|
||||
|
||||
>> "St. Bartholemew's Fair: \'Etude for a Push-Me/Pull-You Machine"
|
||||
|
||||
then what would be a good filename for its released story file?
|
||||
|
||||
In the filename version we must forcibly truncate the text to ensure
|
||||
that it does not exceed a certain length, and must also make it filename-safe,
|
||||
omitting characters used as folder separators on various platforms and
|
||||
(for good measure) removing accents from accented letters, so that we can
|
||||
arrive at a sequence of ASCII characters. Each run of whitespace is also
|
||||
converted to a single space. If this would result in an empty text or only
|
||||
a single space, we return the text "story" instead.
|
||||
|
||||
Our example (if not truncated) then emerges as:
|
||||
|
||||
|St- Bartholemew's Fair- Etude for a Push-Me-Pull-You Machine|
|
||||
|
||||
Note that we do not write any filename extension (e.g., |.z5|) here.
|
||||
|
||||
We change possible filename separators or extension indicators to hyphens,
|
||||
and remove accents from each possible ISO Latin-1 accented letter. This does
|
||||
still mean that the OE and AE digraphs will simply be omitted, while the
|
||||
German eszet will be barbarously shortened to a single "s", but life is
|
||||
just too short to care overmuch about this.
|
||||
|
||||
@<Compile bibliographic text as a truncated filename@> =
|
||||
int i, pos = STREAM_EXTENT(OUT), whitespace_count=0, black_chars_written = 0;
|
||||
int N = 100;
|
||||
#ifdef IF_MODULE
|
||||
N = BIBLIOGRAPHIC_TEXT_TRUNCATION;
|
||||
#endif
|
||||
if (p[0] == '"') p++;
|
||||
for (i=0; p[i]; i++) {
|
||||
if (STREAM_EXTENT(OUT) - pos >= N) break;
|
||||
if ((p[i] == '"') && (p[i+1] == 0)) break;
|
||||
switch(p[i]) {
|
||||
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
|
||||
whitespace_count++;
|
||||
if (whitespace_count == 1) PUT(' ');
|
||||
break;
|
||||
case '?': case '*':
|
||||
if ((p[i+1]) && (p[i+1] != '\"')) PUT('-');
|
||||
break;
|
||||
default: {
|
||||
int charcode = p[i];
|
||||
charcode = Characters::make_filename_safe(charcode);
|
||||
whitespace_count = 0;
|
||||
if (charcode < 128) {
|
||||
PUT(charcode); black_chars_written++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (black_chars_written == 0) WRITE("story");
|
||||
return;
|
||||
|
|
|
@ -757,7 +757,7 @@ int PL::Bibliographic::Release::write_var_to_XML(OUTPUT_STREAM, nonlocal_variabl
|
|||
} else {
|
||||
wording W = ParseTree::get_text(val);
|
||||
int w1 = Wordings::first_wn(W);
|
||||
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(w1));
|
||||
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(w1));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
@ -785,7 +785,7 @@ int PL::Bibliographic::Release::write_var_to_text(OUTPUT_STREAM, nonlocal_variab
|
|||
} else {
|
||||
wording W = ParseTree::get_text(val);
|
||||
int w1 = Wordings::first_wn(W);
|
||||
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(w1));
|
||||
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(w1));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
|
|
@ -274,7 +274,7 @@ void PL::Actions::Index::index_for_extension(OUTPUT_STREAM, source_file *sf, inf
|
|||
int kc = 0;
|
||||
LOOP_OVER(acn, action_name)
|
||||
if (Lexer::file_of_origin(Wordings::first_wn(acn->present_name)) == E->read_into_file)
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Actions", I"action",
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Actions", I"action",
|
||||
acn->present_name);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ void PL::Actions::Patterns::Named::index_for_extension(OUTPUT_STREAM, source_fil
|
|||
int kc = 0;
|
||||
LOOP_OVER(nap, named_action_pattern)
|
||||
if (Lexer::file_of_origin(Wordings::first_wn(nap->text_of_declaration)) == E->read_into_file)
|
||||
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Kinds of action", I"kind of action",
|
||||
kc = Extensions::Files::document_headword(OUT, kc, E, "Kinds of action", I"kind of action",
|
||||
nap->text_of_declaration);
|
||||
if (kc != 0) HTML_CLOSE("p");
|
||||
}
|
||||
|
|
|
@ -570,7 +570,7 @@ void Index::index_actual_element(OUTPUT_STREAM, text_stream *elt) {
|
|||
if (Str::eq_wide_string(elt, L"C")) {
|
||||
Sentences::Headings::index(OUT);
|
||||
Extensions::Files::index(OUT);
|
||||
Extensions::Files::update_census();
|
||||
Extensions::Census::update_census();
|
||||
return;
|
||||
}
|
||||
if (Str::eq_wide_string(elt, L"Vl")) {
|
|
@ -7,13 +7,7 @@ Licence: Artistic License 2.0
|
|||
Chapter 1: Starting Up
|
||||
Index Module
|
||||
|
||||
Chapter 2: HTML
|
||||
"HTML and JavaScript generation."
|
||||
HTML Files
|
||||
Javascript Pastes
|
||||
HTML Documentation
|
||||
|
||||
Chapter 3: Indexing
|
||||
Chapter 2: Indexing
|
||||
"Utility routines for generating index pages."
|
||||
Index File Services
|
||||
Documentation References
|
||||
|
|
|
@ -61,6 +61,7 @@ INBUILDX = inbuild/Tangled/inbuild
|
|||
{module} INTER inter inter/inter-module
|
||||
{module} BUILDING building inter/building-module
|
||||
{module} CODEGEN codegen inter/codegen-module
|
||||
{module} HTML html inbuild/html-module
|
||||
{module} ARCH arch inbuild/arch-module
|
||||
{module} INBUILD inbuild inbuild/inbuild-module
|
||||
|
||||
|
@ -88,6 +89,7 @@ INBUILDX = inbuild/Tangled/inbuild
|
|||
{dep} INFORM7 on IF
|
||||
{dep} INFORM7 on MULTIMEDIA
|
||||
{dep} INFORM7 on INDEX
|
||||
{dep} INFORM7 on HTML
|
||||
{dep} INFORM7 on ARCH
|
||||
{dep} INFORM7 on INTER
|
||||
{dep} INFORM7 on BUILDING
|
||||
|
@ -103,6 +105,7 @@ INBUILDX = inbuild/Tangled/inbuild
|
|||
{tool} INBUILDTOOL inbuild inbuild
|
||||
{dep} INBUILDTOOL on FOUNDATION
|
||||
{dep} INBUILDTOOL on WORDS
|
||||
{dep} INBUILDTOOL on HTML
|
||||
{dep} INBUILDTOOL on ARCH
|
||||
{dep} INBUILDTOOL on INBUILD
|
||||
|
||||
|
|
Loading…
Reference in a new issue