/* * 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); } //////////////////////////////// typedef u32 Mangle_Rule; enum{ MangleRule_None, MangleRule_MacroSig, MangleRule_ToLower, }; internal Mangle_Rule get_mangle_rule(String mangle){ Mangle_Rule result = MangleRule_None; if (match(mangle, "macro sig")){ result = MangleRule_MacroSig; } else if (match(mangle, "to lower")){ result = MangleRule_ToLower; } return(result); } internal String apply_mangle_rule(String name, u32 mangle_rule){ String result = {0}; switch (mangle_rule){ case MangleRule_MacroSig: { result = str_alloc(name.size + 5); fm_align(); copy(&result, name); to_upper(&result); append(&result, "_SIG"); terminate_with_null(&result); }break; case MangleRule_ToLower: { result = str_alloc(name.size + 1); fm_align(); copy(&result, name); to_lower(&result); terminate_with_null(&result); }break; default: { result = name; }break; } return(result); } //////////////////////////////// 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; u32 mangle_rule; } 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_MetaUnit, // 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; // Meta parse members Meta_Unit *unit; }; 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{ char *code_dir; char *asset_dir; char *src_dir; Basic_List doc_list; Basic_List img_list; Basic_List file_list; Basic_List meta_list; Basic_List unresolved_includes; }; internal Document_System create_document_system(char *code_dir, char *asset_dir, char *src_dir){ Document_System system = {0}; system.code_dir = code_dir; system.asset_dir = asset_dir; system.src_dir = src_dir; 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 char* get_null_terminated_version(String str){ char *ptr = 0; if (terminate_with_null(&str)){ ptr = str.str; } else{ String b = str_alloc(str.size + 1); copy(&b, str); terminate_with_null(&b); ptr = b.str; } return(ptr); } internal b32 create_meta_unit(Document_System *doc_system, String name_str, String file_str){ char *name = get_null_terminated_version(name_str); char *file = get_null_terminated_version(file_str); Abstract_Item *item = create_abstract_item(&doc_system->meta_list, name); b32 result = false; if (item != 0){ Meta_Unit *unit = fm_push_array(Meta_Unit, 1); *unit = compile_meta_unit(doc_system->code_dir, file, ExpandArray(meta_keywords)); if (unit->count != 0){ result = true; item->item_type = ItemType_MetaUnit; item->name = name; item->unit = unit; } } 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){ char *full_file = fm_str(system->asset_dir, "/", source_file); item->item_type = ItemType_GenericFile; item->extension = extension; item->source_file = full_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){ char *full_file = fm_str(system->asset_dir, "/", source_file); item->item_type = ItemType_Image; item->name = name; item->extension = extension; item->source_file = full_file; i32 w = 0, h = 0, comp = 0; i32 stbi_r = stbi_info(full_file, &w, &h, &comp); if (!stbi_r){ fprintf(stdout, "Did not find file %s\n", full_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_doc_list(Document_Builder *builder, Meta_Unit *unit, Mangle_Rule mangle_rule){ Document_Item *item = doc_new_item(builder, Doc_DocList); item->unit_elements.unit = unit; item->unit_elements.mangle_rule = mangle_rule; } internal void add_doc_full(Document_Builder *builder, Meta_Unit *unit, Mangle_Rule mangle_rule){ Document_Item *item = doc_new_item(builder, Doc_DocFull); item->unit_elements.unit = unit; item->unit_elements.mangle_rule = mangle_rule; } 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){ doc_new_item_strings(builder, Doc_DocumentLink, text, null_string); } 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, Cmd_MetaParse, Cmd_DocList, Cmd_DocFull, // 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"); enriched_commands_global_array[Cmd_MetaParse] = make_lit_string("META_PARSE"); enriched_commands_global_array[Cmd_DocList] = make_lit_string("DOC_LIST"); enriched_commands_global_array[Cmd_DocFull] = make_lit_string("DOC_FULL"); } 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; case Cmd_MetaParse: { String name = {0}; String file = {0}; if (extract_command_body(l, &i, &name)){ if (extract_command_body(l, &i, &file)){ if (!create_meta_unit(doc_system, name, file)){ char space[512]; String str = make_fixed_width_string(space); append(&str, "parse failed for "); append(&str, file); add_error(&builder, str); } } else{ report_error_missing_body(&builder, command_string); } } else{ report_error_missing_body(&builder, command_string); } }break; case Cmd_DocList: case Cmd_DocFull: { String name = {0}; if (extract_command_body(l, &i, &name)){ String mangle = {0}; extract_command_body(l, &i, &mangle); Abstract_Item *item = get_item_by_name(doc_system->meta_list, name); if (item != 0){ u32 mangle_rule = MangleRule_None; if (match_part(mangle, "mangle:")){ String mangle_name = substr_tail(mangle, sizeof("mangle:")-1); mangle_name = skip_chop_whitespace(mangle_name); mangle_rule = get_mangle_rule(mangle_name); } if (match_index == Cmd_DocList){ add_doc_list(&builder, item->unit, mangle_rule); } else{ add_doc_full(&builder, item->unit, mangle_rule); } } } 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){ 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(doc_system->src_dir, 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 "
"); } 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,"