/* * Mr. 4th Dimention - Allen Webster * * 25.02.2016 * * File editing view for 4coder * */ // TOP struct Enriched_Text{ String fname; String source; }; internal Enriched_Text load_enriched_text(char *directory, char *filename){ Enriched_Text result = {0}; char *fname = fm_str(directory, "/", filename); result.fname = str_alloc(str_size(fname) + 1); fm_align(); copy(&result.fname, fname); terminate_with_null(&result.fname); result.source = file_dump(fname); return(result); } //////////////////////////////// struct Alternate_Name{ String macro; String public_name; }; struct Alternate_Names_Array{ Alternate_Name *names; }; enum{ AltName_Standard, AltName_Macro, AltName_Public_Name, }; //////////////////////////////// enum{ Doc_Root, Doc_Section, Doc_Error, Doc_Todo, Doc_Include, Doc_DocList, Doc_DocFull, Doc_TableOfContents, Doc_PlainOldText, Doc_Version, Doc_BeginStyle, Doc_EndStyle, Doc_DocumentLink, Doc_BeginLink, Doc_EndLink, Doc_Image, Doc_Video, Doc_BeginParagraph, Doc_EndParagraph, Doc_BeginList, Doc_EndList, Doc_BeginItem, Doc_EndItem, // Doc_COUNT, }; struct Document_Item{ Document_Item *next; Document_Item *parent; i32 type; union{ struct{ Document_Item *first_child; Document_Item *last_child; String name; String id; b32 show_title; } section; struct{ Meta_Unit *unit; Alternate_Names_Array *alt_names; i32 alt_name_type; } unit_elements; struct{ String string; String string2; } string; struct{ String name; struct Abstract_Item *document; } include; struct{ Enriched_Text *text; } enriched_text; }; }; global Document_Item null_document_item = {0}; //////////////////////////////// struct Basic_Node{ Basic_Node *next; }; #define NodeGetData(node, T) ((T*) ((node)+1)) struct Basic_List{ Basic_Node *head; Basic_Node *tail; u32 count; }; internal void clear_list(Basic_List *list){ memset(list, 0, sizeof(*list)); } internal void* push_item_on_list(Basic_List *list, i32 item_size){ i32 mem_size = item_size + sizeof(Basic_Node); void *mem = fm__push(mem_size); Assert(mem != 0); memset(mem, 0, mem_size); Basic_Node *node = (Basic_Node*)mem; if (list->head == 0){ list->head = node; list->tail = node; } else{ list->tail->next = node; list->tail = node; } ++list->count; void *result = (node + 1); return(result); } //////////////////////////////// enum{ ItemType_Document, ItemType_Image, ItemType_GenericFile, // ItemType_COUNT, }; struct Abstract_Item{ i32 item_type; char *name; // Document value members Document_Item *root_item; // Image value members char *source_file; char *extension; float w_h_ratio; float h_w_ratio; Basic_List img_instantiations; }; global Abstract_Item null_abstract_item = {0}; struct Abstract_Item_Array{ Abstract_Item **items; u32 count; }; internal Abstract_Item_Array get_abstract_item_array(Basic_List *list){ Abstract_Item_Array result = {0}; result.items = (Abstract_Item**)fm_push_array(Abstract_Item*, list->count); result.count = list->count; u32 i = 0; for (Basic_Node *node = list->head; node != 0; node = node->next){ result.items[i++] = NodeGetData(node, Abstract_Item); } return(result); } struct Document_Builder{ Abstract_Item *doc; Document_Item *section_stack[16]; i32 section_top; }; struct Image_Instantiation{ i32 w, h; }; struct Document_System{ Basic_List doc_list; Basic_List img_list; Basic_List file_list; Basic_List unresolved_includes; }; internal Document_System create_document_system(){ Document_System system = {0}; return(system); } internal void create_unresolved_include(Document_System *doc_system, Document_Item *include_item){ Document_Item **new_item = (Document_Item**)push_item_on_list(&doc_system->unresolved_includes, sizeof(*new_item)); *new_item = include_item; } internal Abstract_Item* get_item_by_name(Basic_List list, String name){ Abstract_Item *result = 0; for (Basic_Node *node = list.head; node != 0; node = node->next){ Abstract_Item *item = NodeGetData(node, Abstract_Item); if (match(item->name, name)){ result = item; break; } } return(result); } internal Image_Instantiation* get_image_instantiation(Basic_List list, i32 w, i32 h){ Image_Instantiation *result = 0; for (Basic_Node *node = list.head; node != 0; node = node->next){ Image_Instantiation *instantiation = NodeGetData(node, Image_Instantiation); if (instantiation->w == w && instantiation->h == h){ result = instantiation; break; } } return(result); } internal void add_image_instantiation(Basic_List *list, i32 w, i32 h){ Image_Instantiation *instantiation = (Image_Instantiation*)push_item_on_list(list, sizeof(*instantiation)); instantiation->w = w; instantiation->h = h; } internal Abstract_Item* create_abstract_item(Basic_List *list, char *name){ Abstract_Item *result = 0; Abstract_Item *lookup = get_item_by_name(*list, make_string_slowly(name)); if (lookup == 0){ result = (Abstract_Item*)push_item_on_list(list, sizeof(*result)); } return(result); } internal Abstract_Item* add_generic_file(Document_System *system, char *source_file, char *extension, char *name){ Abstract_Item *item = create_abstract_item(&system->file_list, name); if (item){ item->item_type = ItemType_GenericFile; item->extension = extension; item->source_file = source_file; item->name = name; } return(item); } internal Abstract_Item* add_image_description(Document_System *system, char *source_file, char *extension, char *name){ Abstract_Item *item = create_abstract_item(&system->img_list, name); if (item != 0){ item->item_type = ItemType_Image; item->extension = extension; item->source_file = source_file; item->name = name; i32 w = 0, h = 0, comp = 0; i32 stbi_r = stbi_info(source_file, &w, &h, &comp); if (!stbi_r){ fprintf(stdout, "Did not find file %s\n", source_file); item->w_h_ratio = 1.f; item->h_w_ratio = 1.f; } else{ item->w_h_ratio = ((float)w/(float)h); item->h_w_ratio = ((float)h/(float)w); } } return(item); } internal void set_item_string(String *out, String text){ *out = str_alloc(text.size + 1); fm_align(); copy(out, text); terminate_with_null(out); } internal void set_section_name(Document_Item *item, char *name, b32 show_title){ set_item_string(&item->section.name, make_string_slowly(name)); item->section.show_title = show_title; } internal void set_section_id(Document_Item *item, char *id){ set_item_string(&item->section.id, make_lit_string(id)); } internal Document_Builder begin_document_description(Document_System *system, char *title, char *name, b32 show_title){ Document_Builder builder = {0}; Abstract_Item *doc = create_abstract_item(&system->doc_list, name); if (doc != 0){ builder.doc = doc; *doc = null_abstract_item; doc->item_type = ItemType_Document; doc->root_item = fm_push_array(Document_Item, 1); *doc->root_item = null_document_item; set_section_name(doc->root_item, title, show_title); doc->name = name; doc->root_item->type = Doc_Root; builder.section_stack[builder.section_top] = doc->root_item; } return(builder); } internal void end_document_description(Document_Builder *builder){ Assert(builder->section_top == 0); } internal void append_child(Document_Item *parent, Document_Item *item){ Assert(parent->type == Doc_Root || parent->type == Doc_Section); if (parent->section.last_child == 0){ parent->section.first_child = item; } else{ parent->section.last_child->next = item; } parent->section.last_child = item; item->parent = parent; } internal Document_Item* doc_new_item(Document_Builder *builder, u32 type){ Assert(builder->section_top + 1 < ArrayCount(builder->section_stack)); Document_Item *parent = builder->section_stack[builder->section_top]; Document_Item *item = fm_push_array(Document_Item, 1); *item = null_document_item; item->type = type; append_child(parent, item); return(item); } internal Document_Item* doc_new_item_strings(Document_Builder *builder, u32 type, String s1, String s2){ Document_Item *item = doc_new_item(builder, type); if (s1.size > 0){ set_item_string(&item->string.string, s1); } if (s2.size > 0){ set_item_string(&item->string.string2, s2); } return(item); } internal void begin_section(Document_Builder *builder, char *title, char *id){ Document_Item *section = doc_new_item(builder, Doc_Section); builder->section_stack[++builder->section_top] = section; set_section_name(section, title, true); if (id != 0){ set_section_id(section, id); } } internal void end_section(Document_Builder *builder){ Assert(builder->section_top > 0); --builder->section_top; } internal void add_error(Document_Builder *builder, String text){ doc_new_item_strings(builder, Doc_Error, text, null_string); } internal void report_error_missing_body(Document_Builder *builder, String command_body){ char space[512]; String error_string = make_fixed_width_string(space); append(&error_string, "missing body for "); append(&error_string, command_body); add_error(builder, error_string); } internal void add_todo(Document_Builder *builder){ doc_new_item(builder, Doc_Todo); } internal void add_include(Document_System *doc_system, Document_Builder *builder, String text){ Document_Item *item = doc_new_item(builder, Doc_Include); set_item_string(&item->include.name, text); create_unresolved_include(doc_system, item); } internal void add_element_list(Document_Builder *builder, Meta_Unit *unit){ Document_Item *item = doc_new_item(builder, Doc_DocList); item->unit_elements.unit = unit; } internal void add_element_list(Document_Builder *builder, Meta_Unit *unit, Alternate_Names_Array *alt_names, i32 alt_name_type){ Document_Item *item = doc_new_item(builder, Doc_DocList); item->unit_elements.unit = unit; item->unit_elements.alt_names = alt_names; item->unit_elements.alt_name_type = alt_name_type; } internal void add_full_elements(Document_Builder *builder, Meta_Unit *unit){ Document_Item *item = doc_new_item(builder, Doc_DocFull); item->unit_elements.unit = unit; } internal void add_full_elements(Document_Builder *builder, Meta_Unit *unit, Alternate_Names_Array *alt_names, i32 alt_name_type){ Document_Item *item = doc_new_item(builder, Doc_DocFull); item->unit_elements.unit = unit; item->unit_elements.alt_names = alt_names; item->unit_elements.alt_name_type = alt_name_type; } internal void add_table_of_contents(Document_Builder *builder){ doc_new_item(builder, Doc_TableOfContents); } internal void add_plain_old_text(Document_Builder *builder, String text){ doc_new_item_strings(builder, Doc_PlainOldText, text, null_string); } internal void add_enriched_text(Document_Builder *builder, Enriched_Text *text){ Document_Item *item = doc_new_item(builder, Doc_PlainOldText); item->enriched_text.text = text; } internal void add_version(Document_Builder *builder){ doc_new_item(builder, Doc_Version); } internal void add_begin_style(Document_Builder *builder, String text){ doc_new_item_strings(builder, Doc_BeginStyle, text, null_string); } internal void add_end_style(Document_Builder *builder){ doc_new_item(builder, Doc_EndStyle); } internal void add_document_link(Document_Builder *builder, String text){ Document_Item *item = doc_new_item(builder, Doc_BeginStyle); item->string.string = str_alloc(text.size); fm_align(); copy(&item->string.string, text); } internal void add_begin_link(Document_Builder *builder, String text){ doc_new_item_strings(builder, Doc_BeginLink, text, null_string); } internal void add_end_link(Document_Builder *builder){ doc_new_item(builder, Doc_EndLink); } internal void add_image(Document_Builder *builder, String text, String extra_text){ doc_new_item_strings(builder, Doc_Image, text, extra_text); } internal void add_video(Document_Builder *builder, String text){ doc_new_item_strings(builder, Doc_Video, text, null_string); } internal void add_begin_paragraph(Document_Builder *builder){ doc_new_item(builder, Doc_BeginParagraph); } internal void add_end_paragraph(Document_Builder *builder){ doc_new_item(builder, Doc_EndParagraph); } internal void add_begin_list(Document_Builder *builder){ doc_new_item(builder, Doc_BeginList); } internal void add_end_list(Document_Builder *builder){ doc_new_item(builder, Doc_EndList); } internal void add_begin_item(Document_Builder *builder){ doc_new_item(builder, Doc_BeginItem); } internal void add_end_item(Document_Builder *builder){ doc_new_item(builder, Doc_EndItem); } // Document Generation from Enriched Text enum Command_Types{ Cmd_BackSlash, Cmd_BeginStyle, Cmd_EndStyle, Cmd_DocumentLink, Cmd_BeginList, Cmd_EndList, Cmd_BeginItem, Cmd_EndItem, Cmd_BeginLink, Cmd_EndLink, Cmd_Image, Cmd_Video, Cmd_Section, Cmd_EndSection, Cmd_Version, Cmd_TableOfContents, Cmd_Todo, Cmd_Include, // never below this Cmd_COUNT, }; global b32 did_enriched_commands = false; global String enriched_commands_global_array[Cmd_COUNT]; internal String* get_enriched_commands(){ if (!did_enriched_commands){ did_enriched_commands = true; enriched_commands_global_array[Cmd_BackSlash] = make_lit_string("\\"); enriched_commands_global_array[Cmd_BeginStyle] = make_lit_string("BEGIN_STYLE"); enriched_commands_global_array[Cmd_EndStyle] = make_lit_string("END_STYLE"); enriched_commands_global_array[Cmd_DocumentLink] = make_lit_string("DOC_LINK"); enriched_commands_global_array[Cmd_BeginList] = make_lit_string("BEGIN_LIST"); enriched_commands_global_array[Cmd_EndList] = make_lit_string("END_LIST"); enriched_commands_global_array[Cmd_BeginItem] = make_lit_string("BEGIN_ITEM"); enriched_commands_global_array[Cmd_EndItem] = make_lit_string("END_ITEM"); enriched_commands_global_array[Cmd_BeginLink] = make_lit_string("BEGIN_LINK"); enriched_commands_global_array[Cmd_EndLink] = make_lit_string("END_LINK"); enriched_commands_global_array[Cmd_Image] = make_lit_string("IMAGE"); enriched_commands_global_array[Cmd_Video] = make_lit_string("VIDEO"); enriched_commands_global_array[Cmd_Section] = make_lit_string("SECTION"); enriched_commands_global_array[Cmd_EndSection] = make_lit_string("END_SECTION"); enriched_commands_global_array[Cmd_Version] = make_lit_string("VERSION"); enriched_commands_global_array[Cmd_TableOfContents] = make_lit_string("TABLE_OF_CONTENTS"); enriched_commands_global_array[Cmd_Todo] = make_lit_string("TODO"); enriched_commands_global_array[Cmd_Include] = make_lit_string("INCLUDE"); } return(enriched_commands_global_array); } internal u32 get_enriched_commands_count(){ return(ArrayCount(enriched_commands_global_array)); } internal b32 extract_command_body(String l, i32 *i_in_out, i32 *body_start_out, i32 *body_end_out){ b32 result = false; i32 i = *i_in_out; for (; i < l.size; ++i){ if (!char_is_whitespace(l.str[i])){ break; } } i32 body_start = 0, body_end = 0; if (l.str[i] == '{'){ body_start = i + 1; for (++i; i < l.size; ++i){ if (l.str[i] == '}'){ result = true; body_end = i; ++i; break; } } } if (result){ *i_in_out = i; *body_start_out = body_start; *body_end_out = body_end; } return(result); } internal b32 extract_command_body(String l, i32 *i_in_out, String *body_text_out){ i32 body_start = 0, body_end = 0; b32 has_body = extract_command_body(l, i_in_out, &body_start, &body_end); if (has_body){ String body_text = substr(l, body_start, body_end - body_start); body_text = skip_chop_whitespace(body_text); *body_text_out = body_text; } return(has_body); } internal Abstract_Item* make_document_from_text(Document_System *doc_system, char *title, char *name, Enriched_Text *text){ String source = text->source; Document_Builder builder = begin_document_description(doc_system, title, name, false); if (source.str == 0){ char space[512]; String str = make_fixed_width_string(space); copy(&str, "could not open source file "); copy(&str, text->fname); add_error(&builder, str); } else{ for (String line = get_first_double_line(source); line.str; line = get_next_double_line(source, line)){ String l = skip_chop_whitespace(line); if (l.size == 0) continue; add_begin_paragraph(&builder); i32 start = 0, i = 0; for (; i < l.size; ++i){ char ch = l.str[i]; if (ch == '\\'){ add_plain_old_text(&builder, substr(l, start, i - start)); i32 command_start = i + 1; i32 command_end = command_start; for (; command_end < l.size; ++command_end){ if (!char_is_alpha_numeric(l.str[command_end])){ break; } } if (command_end == command_start){ if (command_end < l.size && l.str[command_end] == '\\'){ ++command_end; } } String command_string = substr(l, command_start, command_end - command_start); String *enriched_commands = get_enriched_commands(); u32 enriched_commands_count = get_enriched_commands_count(); i = command_end; i32 match_index = 0; if (!string_set_match(enriched_commands, enriched_commands_count, command_string, &match_index)){ match_index = -1; } switch (match_index){ case Cmd_BackSlash: { add_plain_old_text(&builder, make_lit_string("\\")); }break; case Cmd_BeginStyle: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ add_begin_style(&builder, body_text); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_EndStyle: { add_end_style(&builder); }break; // TODO(allen): upgrade this bs case Cmd_DocumentLink: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ add_document_link(&builder, body_text); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_BeginList: { add_begin_list(&builder); }break; case Cmd_EndList: { add_end_list(&builder); }break; case Cmd_BeginItem: { add_begin_item(&builder); }break; case Cmd_EndItem: { add_end_item(&builder); }break; case Cmd_BeginLink: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ add_begin_link(&builder, body_text); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_EndLink: { add_end_link(&builder); }break; case Cmd_Image: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ String size_parameter = {0}; extract_command_body(l, &i, &size_parameter); add_image(&builder, body_text, size_parameter); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_Video: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ add_video(&builder, body_text); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_Section: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ String extra_text = {0}; extract_command_body(l, &i, &extra_text); String title = str_alloc(body_text.size + 1); copy(&title, body_text); terminate_with_null(&title); String id = str_alloc(extra_text.size + 1); copy(&id, extra_text); terminate_with_null(&id); begin_section(&builder, title.str, id.str); } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_EndSection: { end_section(&builder); }break; case Cmd_Version: { add_version(&builder); }break; case Cmd_TableOfContents: { add_table_of_contents(&builder); }break; case Cmd_Todo: { add_todo(&builder); }break; case Cmd_Include: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ add_include(doc_system, &builder, body_text); } else{ report_error_missing_body(&builder, command_string); } }break; default: { char space[512]; String error = make_fixed_width_string(space); append(&error, "unrecognized command "); append(&error, command_string); add_error(&builder, error); }break; } start = i; } } if (start != i){ add_plain_old_text(&builder, substr(l, start, i - start)); } add_end_paragraph(&builder); } } end_document_description(&builder); return(builder.doc); } //////////////////////////////// struct Unresolved_Include_Array{ Document_Item **items; u32 count; }; internal Unresolved_Include_Array get_unresolved_includes(Document_System *doc_system){ Unresolved_Include_Array result = {0}; Basic_List *list = &doc_system->unresolved_includes; result.items = (Document_Item**)fm_push_array(Document_Item*, list->count); result.count = list->count; u32 i = 0; for (Basic_Node *node = list->head; node != 0; node = node->next){ result.items[i++] = *NodeGetData(node, Document_Item*); } return(result); } internal void resolve_all_includes(Document_System *doc_system, char *src_directory){ for (;doc_system->unresolved_includes.count > 0;){ Unresolved_Include_Array includes = get_unresolved_includes(doc_system); clear_list(&doc_system->unresolved_includes); Document_Item **item_ptr = includes.items; for (u32 i = 0; i < includes.count; ++i, ++item_ptr){ Document_Item *item = *item_ptr; Assert(item->include.document == 0); Abstract_Item *inc_doc = get_item_by_name(doc_system->doc_list, item->include.name); if (inc_doc == 0){ String source_text = item->include.name; Enriched_Text *text = fm_push_array(Enriched_Text, 1); *text = load_enriched_text(src_directory, source_text.str); inc_doc = make_document_from_text(doc_system, source_text.str, source_text.str, text); } item->include.document = inc_doc; } } } //////////////////////////////// // HTML Document Generation #define HTML_BACK_COLOR "#FAFAFA" #define HTML_TEXT_COLOR "#0D0D0D" #define HTML_CODE_BACK "#DFDFDF" #define HTML_EXAMPLE_BACK "#EFEFDF" #define HTML_POP_COLOR_1 "#309030" #define HTML_POP_BACK_1 "#E0FFD0" #define HTML_VISITED_LINK "#A0C050" #define HTML_POP_COLOR_2 "#005000" #define HTML_CODE_STYLE "font-family: \"Courier New\", Courier, monospace; text-align: left;" #define HTML_CODE_BLOCK_STYLE(back) \ "margin-top: 3mm; margin-bottom: 3mm; font-size: .95em; " \ "background: "back"; padding: 0.25em;" #define HTML_DESCRIPT_SECTION_STYLE HTML_CODE_BLOCK_STYLE(HTML_CODE_BACK) #define HTML_EXAMPLE_CODE_STYLE HTML_CODE_BLOCK_STYLE(HTML_EXAMPLE_BACK) #define HTML_DOC_HEAD_OPEN "
" #define HTML_DOC_HEAD_CLOSE "
" #define HTML_DOC_ITEM_HEAD_STYLE "font-weight: 600;" #define HTML_DOC_ITEM_HEAD_INL_OPEN "" #define HTML_DOC_ITEM_HEAD_INL_CLOSE "" #define HTML_DOC_ITEM_HEAD_OPEN "
" #define HTML_DOC_ITEM_HEAD_CLOSE "
" #define HTML_DOC_ITEM_OPEN "
" #define HTML_DOC_ITEM_CLOSE "
" #define HTML_EXAMPLE_CODE_OPEN "
" #define HTML_EXAMPLE_CODE_CLOSE "
" struct Section_Counter{ i32 counter[16]; i32 nest_level; i32 list_item_counter; }; internal b32 doc_get_link_string(Abstract_Item *doc, char *space, i32 capacity){ String str = make_string_cap(space, 0, capacity); append(&str, doc->name); append(&str, ".html"); b32 result = terminate_with_null(&str); return(result); } internal b32 img_get_link_string(Abstract_Item *img, char *space, i32 capacity, i32 w, i32 h){ String str = make_string_cap(space, 0, capacity); append(&str, img->name); append(&str, "_"); append_int_to_str(&str, w); append(&str, "_"); append_int_to_str(&str, h); append(&str, "."); append(&str, img->extension); b32 result = terminate_with_null(&str); return(result); } internal void append_section_number_reduced(String *out, Section_Counter *section_counter, i32 reduce){ i32 level = section_counter->nest_level-reduce; for (i32 i = 1; i <= level; ++i){ append_int_to_str(out, section_counter->counter[i]); if (i != level){ append(out, "."); } } } internal void append_section_number(String *out, Section_Counter *section_counter){ append_section_number_reduced(out, section_counter, 0); } #define ERROR_HTML_START "! generator error: " #define ERROR_HTML_END " !" internal void output_error(String *out, String error){ append(out, ERROR_HTML_START); append(out, error); append(out, ERROR_HTML_END); fprintf(stdout, "error: %.*s\n", error.size, error.str); } internal void report_error_html_missing_body(String *out, String command_name){ char space[512]; String str = make_fixed_width_string(space); append(&str, "missing body for "); append(&str, command_name); output_error(out, str); } internal void html_render_section_header(String *out, String section_name, String section_id, Section_Counter *section_counter){ if (section_counter->nest_level <= 1){ if (section_id.size > 0){ append(out, "\n

§"); } else{ append(out, "\n

§"); } append_section_number(out, section_counter); append(out, " "); append(out, section_name); append(out, "

"); } else{ if (section_id.size > 0){ append(out, "

§"); } else{ append(out, "

§"); } append_section_number(out, section_counter); append(out, " "); append(out, section_name); append(out, "

"); } } #define HTML_WIDTH 800 internal void output_plain_old_text(String *out, String l){ u32 start = 0; u32 i = 0; for (; i < (u32)l.size; ++i){ char ch = l.str[i]; switch (ch){ case '<': { append(out, substr(l, start, i - start)); append(out, "<"); start = i + 1; }break; case '>': { append(out, substr(l, start, i - start)); append(out, ">"); start = i + 1; }break; } } if (start != i){ append(out, substr(l, start, i - start)); } } internal void output_begin_style(String *out, String l){ if (match(l, "code")){ append(out, ""); } else{ fprintf(stdout, "error: unrecognized style\n"); append(out, ""); } } internal void output_end_style(String *out){ append(out, ""); } internal void output_document_link(String *out, String l){ append(out, ""); append(out, l); append(out, ""); } internal void output_begin_link(Document_System *doc_system, String *out, String l){ append(out, ""); } internal void output_end_link(String *out){ append(out, ""); } internal void output_image(Document_System *doc_system, String *out, String l, String l2){ i32 pixel_height = 10; i32 pixel_width = HTML_WIDTH; if (l2.size > 0){ if (match_part(l2, "width:")){ String width_string = substr_tail(l2, sizeof("width:")-1); if (str_is_int(width_string)){ pixel_width = str_to_int(width_string); } } } if (match_part_sc(l, "image:")){ String img_name = substr_tail(l, sizeof("image:")-1); Abstract_Item *img_lookup = get_item_by_name(doc_system->img_list, img_name); if (img_lookup){ pixel_height = ceil32(pixel_width*img_lookup->h_w_ratio); append(out, ""); } } } internal void output_video(String *out, String l){ if (match_part_sc(l, "youtube:")){ i32 pixel_width = HTML_WIDTH; i32 pixel_height = (i32)(pixel_width * 0.5625f); String youtube_str = substr_tail(l, sizeof("youtube:")-1); append(out, ""); } else{ char space[512]; String str = make_fixed_width_string(space); append(&str, "unrecognized video type "); append(&str, l); output_error(out, str); } } internal void output_begin_paragraph(String *out){ append(out, "

"); } internal void output_end_paragraph(String *out){ append(out, "

"); } internal void output_begin_section(String *out, Section_Counter *section_counter, String l, String l2){ html_render_section_header(out, l, l2, section_counter); ++section_counter->nest_level; section_counter->list_item_counter = 0; } internal void output_end_section(String *out, Section_Counter *section_counter){ if (section_counter->nest_level > 0){ --section_counter->nest_level; ++section_counter->counter[section_counter->nest_level]; } else{ output_error(out, make_lit_string("unmatched section end")); } } internal void output_begin_list(String *out){ append(out,""); } internal void output_begin_item(String *out, Section_Counter *section_counter){ if (section_counter->list_item_counter == 0){ append(out, "
  • "); ++section_counter->list_item_counter; } else{ append(out, "
  • "); section_counter->list_item_counter = 0; } } internal void output_end_item(String *out){ append(out, "
  • "); } internal void write_enriched_text_html(String *out, Enriched_Text *text, Document_System *doc_system, Section_Counter *section_counter){ String source = text->source; append(out, "
    "); for (String line = get_first_double_line(source); line.str; line = get_next_double_line(source, line)){ String l = skip_chop_whitespace(line); output_begin_paragraph(out); i32 start = 0, i = 0; for (; i < l.size; ++i){ char ch = l.str[i]; if (ch == '\\'){ output_plain_old_text(out, substr(l, start, i - start)); i32 command_start = i + 1; i32 command_end = command_start; for (; command_end < l.size; ++command_end){ if (!char_is_alpha_numeric(l.str[command_end])){ break; } } if (command_end == command_start){ if (command_end < l.size && l.str[command_end] == '\\'){ ++command_end; } } String command_string = substr(l, command_start, command_end - command_start); String *enriched_commands = get_enriched_commands(); u32 enriched_commands_count = get_enriched_commands_count(); i = command_end; i32 match_index = 0; if (!string_set_match(enriched_commands, enriched_commands_count, command_string, &match_index)){ match_index = -1; } switch (match_index){ case Cmd_BackSlash: append(out, "\\"); break; case Cmd_BeginStyle: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ output_begin_style(out, body_text); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_EndStyle: { output_end_style(out); }break; // TODO(allen): upgrade this bs case Cmd_DocumentLink: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ output_document_link(out, body_text); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_BeginList: { output_begin_list(out); }break; case Cmd_EndList: { output_end_list(out); }break; case Cmd_BeginItem: { output_begin_item(out, section_counter); }break; case Cmd_EndItem: { output_end_item(out); }break; case Cmd_BeginLink: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ output_begin_link(doc_system, out, body_text); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_EndLink: { output_end_link(out); }break; case Cmd_Image: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ String size_parameter = {0}; extract_command_body(l, &i, &size_parameter); output_image(doc_system, out, body_text, size_parameter); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_Video: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ output_video(out, body_text); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_Section: { String body_text = {0}; b32 has_body = extract_command_body(l, &i, &body_text); if (has_body){ String extra_text = {0}; extract_command_body(l, &i, &extra_text); output_begin_section(out, section_counter, body_text, extra_text); } else{ report_error_html_missing_body(out, command_string); } }break; case Cmd_EndSection: { output_end_section(out, section_counter); }break; case Cmd_Version: { append(out, VERSION); }break; case Cmd_TableOfContents: { String str = make_lit_string("Cmd_TableOfContents does not work in this system"); output_error(out, str); }break; default: { char space[512]; String str = make_fixed_width_string(space); append(&str, "unrecognized command "); append(&str, command_string); output_error(out, str); }break; } start = i; } } if (start != i){ output_plain_old_text(out, substr(l, start, i - start)); } output_end_paragraph(out); } append(out, "
    "); } internal void print_item_in_list(String *out, String name, char *id_postfix){ append(out, "
  • "); append(out, name); append(out, "
  • "); } internal void init_used_links(Used_Links *used, i32 count){ used->strs = fm_push_array(String, count); used->count = 0; used->max = count; } internal b32 try_to_use_link(Used_Links *used, String str){ b32 result = true; i32 index = 0; if (string_set_match(used->strs, used->count, str, &index)){ result = false; } else{ Assert(used->count < used->max); used->strs[used->count++] = str; } return(result); } internal void print_struct_html(String *out, Item_Node *member, i32 hide_children){ String name = member->name; String type = member->type; String type_postfix = member->type_postfix; append (out, type); append_s_char (out, ' '); append (out, name); append (out, type_postfix); if (match_ss(type, make_lit_string("struct")) || match_ss(type, make_lit_string("union"))){ if (hide_children){ append(out, " { /* non-public internals */ } ;"); } else{ append(out, " {
    "); for (Item_Node *member_iter = member->first_child; member_iter != 0; member_iter = member_iter->next_sibling){ print_struct_html(out, member_iter, hide_children); } append(out, "
    };
    "); } } else{ append(out, ";
    "); } } internal void print_function_html(String *out, Used_Links *used, String cpp_name, String ret, char *function_call_head, String name, Argument_Breakdown breakdown){ append (out, ret); append_s_char (out, ' '); append (out, function_call_head); append (out, name); if (breakdown.count == 0){ append(out, "()"); } else if (breakdown.count == 1){ append(out, "("); append(out, breakdown.args[0].param_string); append(out, ")"); } else{ append(out, "(
    "); for (i32 j = 0; j < breakdown.count; ++j){ append(out, breakdown.args[j].param_string); if (j < breakdown.count - 1){ append_s_char(out, ','); } append(out, "
    "); } append(out, "
    )"); } } internal void print_macro_html(String *out, String name, Argument_Breakdown breakdown){ append (out, "#define "); append (out, name); if (breakdown.count == 0){ append(out, "()"); } else if (breakdown.count == 1){ append_s_char (out, '('); append (out, breakdown.args[0].param_string); append_s_char (out, ')'); } else{ append (out, "(
    "); for (i32 j = 0; j < breakdown.count; ++j){ append(out, breakdown.args[j].param_string); if (j < breakdown.count - 1){ append_s_char(out, ','); } append(out, "
    "); } append(out, ")
    )"); } } enum Doc_Chunk_Type{ DocChunk_PlainText, DocChunk_CodeExample, DocChunk_Count }; global String doc_chunk_headers[] = { make_lit_string(""), make_lit_string("CODE_EXAMPLE"), }; internal String get_next_doc_chunk(String source, String prev_chunk, Doc_Chunk_Type *type){ String chunk = {0}; String word = {0}; i32 pos = source.size; i32 word_index = 0; Doc_Chunk_Type t = DocChunk_PlainText; i32 start_pos = (i32)(prev_chunk.str - source.str) + prev_chunk.size; String source_tail = substr_tail(source, start_pos); Assert(DocChunk_Count == ArrayCount(doc_chunk_headers)); for (word = get_first_word(source_tail); word.str; word = get_next_word(source_tail, word), ++word_index){ for (i32 i = 1; i < DocChunk_Count; ++i){ if (match_ss(word, doc_chunk_headers[i])){ pos = (i32)(word.str - source.str); t = (Doc_Chunk_Type)i; goto doublebreak; } } } doublebreak:; *type = DocChunk_PlainText; if (word_index == 0){ *type = t; i32 nest_level = 1; i32 i = find_s_char(source, pos, '('); for (++i; i < source.size; ++i){ if (source.str[i] == '('){ ++nest_level; } else if (source.str[i] == ')'){ --nest_level; if (nest_level == 0){ break; } } } pos = i+1; } chunk = substr(source, start_pos, pos - start_pos); i32 is_all_white = 1; for (i32 i = 0; i < chunk.size; ++i){ if (!char_is_whitespace(chunk.str[i])){ is_all_white = 0; break; } } if (is_all_white){ chunk = null_string; } return(chunk); } internal String get_first_doc_chunk(String source, Doc_Chunk_Type *type){ String start_str = make_string(source.str, 0); String chunk = get_next_doc_chunk(source, start_str, type); return(chunk); } internal void print_doc_description(String *out, String src){ Doc_Chunk_Type type; for (String chunk = get_first_doc_chunk(src, &type); chunk.str; chunk = get_next_doc_chunk(src, chunk, &type)){ switch (type){ case DocChunk_PlainText: { for (String line = get_first_double_line(chunk); line.str; line = get_next_double_line(chunk, line)){ append(out, line); append(out, "

    "); } }break; case DocChunk_CodeExample: { i32 start = 0; i32 end = chunk.size-1; while (start < end && chunk.str[start] != '(') ++start; start += 1; while (end > start && chunk.str[end] != ')') --end; append(out, HTML_EXAMPLE_CODE_OPEN); if (start < end){ String code_example = substr(chunk, start, end - start); i32 first_line = 1; for (String line = get_first_line(code_example); line.str; line = get_next_line(code_example, line)){ if (!(first_line && line.size == 0)){ i32 space_i = 0; for (; space_i < line.size; ++space_i){ if (line.str[space_i] == ' '){ append(out, " "); } else{ break; } } String line_tail = substr_tail(line, space_i); append(out, line_tail); append(out, "
    "); } first_line = 0; } } append(out, HTML_EXAMPLE_CODE_CLOSE); }break; } } } internal void print_struct_docs(String *out, Item_Node *member){ for (Item_Node *member_iter = member->first_child; member_iter != 0; member_iter = member_iter->next_sibling){ String type = member_iter->type; if (match_ss(type, make_lit_string("struct")) || match_ss(type, make_lit_string("union"))){ print_struct_docs(out, member_iter); } else{ Documentation doc = {0}; perform_doc_parse(member_iter->doc_string, &doc); append(out, "
    "); append(out, "
    "HTML_DOC_ITEM_HEAD_INL_OPEN); append(out, member_iter->name); append(out, HTML_DOC_ITEM_HEAD_INL_CLOSE"
    "); append(out, "
    "HTML_DOC_ITEM_OPEN); print_doc_description(out, doc.main_doc); append(out, HTML_DOC_ITEM_CLOSE"
    "); append(out, "
    "); } } } internal void print_see_also(String *out, Documentation *doc){ i32 doc_see_count = doc->see_also_count; if (doc_see_count > 0){ append(out, HTML_DOC_HEAD_OPEN"See Also"HTML_DOC_HEAD_CLOSE); for (i32 j = 0; j < doc_see_count; ++j){ String see_also = doc->see_also[j]; append(out, HTML_DOC_ITEM_OPEN""); append(out, see_also); append(out, ""HTML_DOC_ITEM_CLOSE); } } } internal void print_function_docs(String *out, String name, String doc_string){ if (doc_string.size == 0){ append(out, "No documentation generated for this function."); fprintf(stdout, "warning: no documentation string for %.*s\n", name.size, name.str); } Temp temp = fm_begin_temp(); Documentation doc = {0}; perform_doc_parse(doc_string, &doc); i32 doc_param_count = doc.param_count; if (doc_param_count > 0){ append(out, HTML_DOC_HEAD_OPEN"Parameters"HTML_DOC_HEAD_CLOSE); for (i32 j = 0; j < doc_param_count; ++j){ String param_name = doc.param_name[j]; String param_docs = doc.param_docs[j]; // TODO(allen): check that param_name is actually a parameter to this function! append(out, "
    "HTML_DOC_ITEM_HEAD_OPEN); append(out, param_name); append(out, HTML_DOC_ITEM_HEAD_CLOSE"
    "HTML_DOC_ITEM_OPEN); append(out, param_docs); append(out, HTML_DOC_ITEM_CLOSE"
    "); } } String ret_doc = doc.return_doc; if (ret_doc.size != 0){ append(out, HTML_DOC_HEAD_OPEN"Return"HTML_DOC_HEAD_CLOSE HTML_DOC_ITEM_OPEN); append(out, ret_doc); append(out, HTML_DOC_ITEM_CLOSE); } String main_doc = doc.main_doc; if (main_doc.size != 0){ append(out, HTML_DOC_HEAD_OPEN"Description"HTML_DOC_HEAD_CLOSE HTML_DOC_ITEM_OPEN); print_doc_description(out, main_doc); append(out, HTML_DOC_ITEM_CLOSE); } print_see_also(out, &doc); fm_end_temp(temp); } internal void print_item_html(String *out, Used_Links *used, Item_Node *item, char *id_postfix, char *section, i32 I, Alternate_Name *alt_name, i32 alt_name_type){ Temp temp = fm_begin_temp(); String name = item->name; switch (alt_name_type){ case AltName_Macro: { name = alt_name->macro; }break; case AltName_Public_Name: { name = alt_name->public_name; }break; } /* NOTE(allen): Open a div for the whole item. Put a heading in it with the name and section. Open a "descriptive" box for the display of the code interface. */ append(out, "
    "); i32 has_cpp_name = 0; if (item->cpp_name.str != 0){ if (try_to_use_link(used, item->cpp_name)){ append(out, "
    "); has_cpp_name = 1; } } append (out, "

    §"); append (out, section); append_s_char (out, '.'); append_int_to_str (out, I); append (out, ": "); append (out, name); append (out, "

    "); append(out, "
    "); switch (item->t){ case Item_Function: { // NOTE(allen): Code box print_function_html(out, used, item->cpp_name, item->ret, "", name, item->breakdown); // NOTE(allen): Close the code box append(out, "
    "); // NOTE(allen): Descriptive section print_function_docs(out, name, item->doc_string); }break; case Item_Macro: { // NOTE(allen): Code box print_macro_html(out, name, item->breakdown); // NOTE(allen): Close the code box append(out, "
    "); // NOTE(allen): Descriptive section print_function_docs(out, name, item->doc_string); }break; case Item_Typedef: { String type = item->type; // NOTE(allen): Code box append (out, "typedef "); append (out, type); append_s_char (out, ' '); append (out, name); append_s_char (out, ';'); // NOTE(allen): Close the code box append(out, "
    "); // NOTE(allen): Descriptive section String doc_string = item->doc_string; Documentation doc = {0}; perform_doc_parse(doc_string, &doc); String main_doc = doc.main_doc; if (main_doc.size != 0){ append(out, HTML_DOC_HEAD_OPEN"Description"HTML_DOC_HEAD_CLOSE); append(out, HTML_DOC_ITEM_OPEN); print_doc_description(out, main_doc); append(out, HTML_DOC_ITEM_CLOSE); } else{ fprintf(stdout, "warning: no documentation string for %.*s\n", name.size, name.str); } print_see_also(out, &doc); }break; case Item_Enum: { // NOTE(allen): Code box append (out, "enum "); append (out, name); append_s_char (out, ';'); // NOTE(allen): Close the code box append(out, ""); // NOTE(allen): Descriptive section String doc_string = item->doc_string; Documentation doc = {0}; perform_doc_parse(doc_string, &doc); String main_doc = doc.main_doc; if (main_doc.size != 0){ append(out, HTML_DOC_HEAD_OPEN"Description"HTML_DOC_HEAD_CLOSE); append(out, HTML_DOC_ITEM_OPEN); print_doc_description(out, main_doc); append(out, HTML_DOC_ITEM_CLOSE); } else{ fprintf(stdout, "warning: no documentation string for %.*s\n", name.size, name.str); } if (item->first_child){ append(out, HTML_DOC_HEAD_OPEN"Values"HTML_DOC_HEAD_CLOSE); for (Item_Node *member = item->first_child; member; member = member->next_sibling){ Documentation doc = {0}; perform_doc_parse(member->doc_string, &doc); append(out, "
    "); // NOTE(allen): Dafuq is this all? append(out, "
    "HTML_DOC_ITEM_HEAD_INL_OPEN); append(out, member->name); append(out, HTML_DOC_ITEM_HEAD_INL_CLOSE); if (member->value.str){ append(out, " = "); append(out, member->value); } append(out, "
    "); append(out, "
    "HTML_DOC_ITEM_OPEN); print_doc_description(out, doc.main_doc); append(out, HTML_DOC_ITEM_CLOSE"
    "); append(out, "
    "); } } print_see_also(out, &doc); }break; case Item_Struct: case Item_Union: { String doc_string = item->doc_string; i32 hide_members = 0; if (doc_string.size == 0){ hide_members = 1; } else{ for (String word = get_first_word(doc_string); word.str; word = get_next_word(doc_string, word)){ if (match_ss(word, make_lit_string("HIDE_MEMBERS"))){ hide_members = 1; break; } } } // NOTE(allen): Code box print_struct_html(out, item, hide_members); // NOTE(allen): Close the code box append(out, ""); // NOTE(allen): Descriptive section { Documentation doc = {0}; perform_doc_parse(doc_string, &doc); String main_doc = doc.main_doc; if (main_doc.size != 0){ append(out, HTML_DOC_HEAD_OPEN"Description"HTML_DOC_HEAD_CLOSE); append(out, HTML_DOC_ITEM_OPEN); print_doc_description(out, main_doc); append(out, HTML_DOC_ITEM_CLOSE); } else{ fprintf(stdout, "warning: no documentation string for %.*s\n", name.size, name.str); } if (!hide_members){ if (item->first_child){ append(out, HTML_DOC_HEAD_OPEN"Fields"HTML_DOC_HEAD_CLOSE); print_struct_docs(out, item); } } print_see_also(out, &doc); } }break; } if (has_cpp_name){ append(out, ""); } // NOTE(allen): Close the item box append(out, "
    "); fm_end_temp(temp); } global char* html_css = ""; struct Include_Stack{ Abstract_Item *stack[512]; u32 top; }; struct Document_Output_System{ String *out; Document_System *doc_system; Used_Links *used_links; Section_Counter *section_counter; Include_Stack *inc_stack; }; internal Document_Output_System make_output_system(String *out, Document_System *doc_system, Used_Links *used_links, Section_Counter *section_counter, Include_Stack *inc_stack){ Document_Output_System sys = {0}; sys.out = out; sys.doc_system = doc_system; sys.used_links = used_links; sys.section_counter = section_counter; sys.inc_stack = inc_stack; return(sys); } internal void doc_item_html(Document_Output_System sys, Document_Item *item, b32 head){ switch (item->type){ case Doc_Root: { if (head){ append(sys.out, "" "" "" ""); append(sys.out, item->section.name); append(sys.out, ""); append(sys.out, html_css); append(sys.out, "\n" "" "
    "); if (item->section.show_title){ append(sys.out, "

    "); append(sys.out, item->section.name); append(sys.out, "

    "); } } else{ append(sys.out, "
    "); } }break; case Doc_Section: { if (head){ html_render_section_header(sys.out, item->section.name, item->section.id, sys.section_counter); } }break; case Doc_Error: { if (head){ output_error(sys.out, item->string.string); } }break; case Doc_Todo: { if (head){ append(sys.out, "
    Coming Soon
    "); } }break; case Doc_Include: { // NOTE(allen): Do nothing. }break; case Doc_DocList: { if (head){ append(sys.out, "
      "); Meta_Unit *unit = item->unit_elements.unit; Alternate_Names_Array *alt_names = item->unit_elements.alt_names; i32 count = unit->set.count; switch (item->unit_elements.alt_name_type){ case AltName_Standard: { for (i32 i = 0; i < count; ++i){ print_item_in_list(sys.out, unit->set.items[i].name, "_doc"); } }break; case AltName_Macro: { for (i32 i = 0; i < count; ++i){ print_item_in_list(sys.out, alt_names->names[i].macro, "_doc"); } }break; case AltName_Public_Name: { for (i32 i = 0; i < count; ++i){ print_item_in_list(sys.out, alt_names->names[i].public_name, "_doc"); } }break; } append(sys.out, "
    "); } }break; case Doc_DocFull: { if (head){ Meta_Unit *unit = item->unit_elements.unit; Alternate_Names_Array *alt_names = item->unit_elements.alt_names; i32 count = unit->set.count; char section_space[32]; String section_str = make_fixed_width_string(section_space); append_section_number_reduced(§ion_str, sys.section_counter, 1); terminate_with_null(§ion_str); if (alt_names){ i32 I = 1; for (i32 i = 0; i < count; ++i, ++I){ print_item_html(sys.out, sys.used_links, &unit->set.items[i], "_doc", section_str.str, I, &alt_names->names[i], item->unit_elements.alt_name_type); } } else{ i32 I = 1; for (i32 i = 0; i < count; ++i, ++I){ print_item_html(sys.out, sys.used_links, &unit->set.items[i], "_doc", section_str.str, I, 0, 0); } } } }break; case Doc_TableOfContents: { if (head){ append(sys.out, "

    Table of Contents

    "); } }break; case Doc_PlainOldText: { if (head){ output_plain_old_text(sys.out, item->string.string); } }break; case Doc_Version: { if (head){ append(sys.out, VERSION); } }break; case Doc_BeginStyle: { if (head){ output_begin_style(sys.out, item->string.string); } }break; case Doc_EndStyle: { if (head){ output_end_style(sys.out); } }break; case Doc_DocumentLink: { if (head){ output_document_link(sys.out, item->string.string); } }break; case Doc_BeginLink: { if (head){ output_begin_link(sys.doc_system, sys.out, item->string.string); } }break; case Doc_EndLink: { if (head){ output_end_link(sys.out); } }break; case Doc_Image: { if (head){ output_image(sys.doc_system, sys.out, item->string.string, item->string.string2); } }break; case Doc_Video: { if (head){ output_video(sys.out, item->string.string); } }break; case Doc_BeginParagraph: { if (head){ output_begin_paragraph(sys.out); } }break; case Doc_EndParagraph: { if (head){ output_end_paragraph(sys.out); } }break; case Doc_BeginList: { if (head){ output_begin_list(sys.out); } }break; case Doc_EndList: { if (head){ output_end_list(sys.out); } }break; case Doc_BeginItem: { if (head){ output_begin_item(sys.out, sys.section_counter); } }break; case Doc_EndItem: { if (head){ output_end_item(sys.out); } }break; } } internal void generate_document_html_inner(Document_Output_System sys, Abstract_Item *doc); internal void generate_item_html(Document_Output_System sys, Document_Item *item){ doc_item_html(sys, item, true); if (item->type == Doc_Root || item->type == Doc_Section){ i32 level = ++sys.section_counter->nest_level; sys.section_counter->counter[level] = 1; for (Document_Item *m = item->section.first_child; m != 0; m = m->next){ generate_item_html(sys, m); } --sys.section_counter->nest_level; ++sys.section_counter->counter[sys.section_counter->nest_level]; } if (item->type == Doc_Include){ Abstract_Item *new_doc = item->include.document; if (new_doc != 0){ b32 duplicate = false; for (u32 i = 0; i < sys.inc_stack->top; ++i){ if (sys.inc_stack->stack[i] == new_doc){ duplicate = true; break; } } if (duplicate){ String error = make_lit_string("recursive inclusion, halted here"); Document_Item temp_item = {0}; temp_item.type = Doc_Error; set_item_string(&temp_item.string.string, error); generate_item_html(sys, &temp_item); } else{ generate_document_html_inner(sys, new_doc); } } } doc_item_html(sys, item, false); } internal void generate_document_html_inner(Document_Output_System sys, Abstract_Item *doc){ Assert(sys.inc_stack->top < ArrayCount(sys.inc_stack->stack)); sys.inc_stack->stack[sys.inc_stack->top++] = doc; generate_item_html(sys, doc->root_item); --sys.inc_stack->top; } internal void generate_document_html(String *out, Document_System *doc_system, Abstract_Item *doc){ Assert(doc->root_item != 0); Used_Links used_links = {0}; init_used_links(&used_links, 4000); Section_Counter section_counter = {0}; section_counter.counter[section_counter.nest_level] = 1; Include_Stack inc_stack = {0}; Document_Output_System sys = make_output_system(out, doc_system, &used_links, §ion_counter, &inc_stack); generate_document_html_inner(sys, doc); } // BOTTOM